+ $output .= $nl . '
- ' . A::t('core', 'Debug') . ' :
- ▲
- ▼
- ☐
- ▣
+
+
+ ' . A::t('core', 'Debug') . ':
+
+
+ ◴ ' . round($totalRunningTime, 3) . ' ' . A::t('core', 'sec') . '
' . A::t('core', 'General') . '
' . A::t('core', 'Params') . ' (' . $totalParams . ')
' . A::t('core', 'Console') . ' (' . $totalConsole . ')
' . A::t('core', 'Warnings') . ' (' . $totalWarnings . ')
' . A::t('core', 'Errors') . ' (' . $totalErrors . ')
- ' . A::t('core', 'SQL Queries') . ' (' . $totalQueries . ')
+ ' . A::t('core', 'SQL Queries') . ' (' . $totalQueries . ')
+
+ ▲
+ ▼
+ ☐
+ ▣
+ ×
@@ -445,13 +497,6 @@ function appExpandTabs(act, key){
$output .= A::t('core', 'PHP version') . ': ' . phpversion() . ' ';
$output .= (CConfig::get('db.driver') != '' ? ucfirst(CConfig::get('db.driver')) . ' ' . A::t('core', 'version') . ': ' . CDatabase::init()->getVersion() : 'DB: ' . A::te('core', 'no')) . ' ';
- $totalRunningTime = round((float)self::$_endTime - (float)self::$_startTime, 5);
- $totalRunningTimeQueriesSql = round((float)self::$_sqlTotalTime, 5);
- $totalRunningTimeConnectionSql = round((float)self::$_sqlConnectionTime, 5);
- $totalRunningTimeScript = round($totalRunningTime - $totalRunningTimeConnectionSql - $totalRunningTimeQueriesSql, 5);
- $totalMemoryUsage = CConvert::fileSize((float)self::$_endMemoryUsage - (float)self::$_startMemoryUsage);
- $htmlCompressionRate = !empty(self::$_arrData['html-compression-rate']) ? self::$_arrData['html-compression-rate'] : A::t('core', 'Unknown');
-
$output .= A::t('core', 'Total running time') . ': ' . $totalRunningTime . ' ' . A::t('core', 'sec') . '. ';
$output .= A::t('core', 'SQL connection running time') . ': ' . $totalRunningTimeConnectionSql . ' ' . A::t('core', 'sec') . '. ';
$output .= A::t('core', 'SQL queries running time') . ': ' . $totalRunningTimeQueriesSql . ' ' . A::t('core', 'sec') . '. ';
@@ -508,7 +553,7 @@ function appExpandTabs(act, key){
$output .= '$_GET :';
$output .= '';
- $arrGet = array();
+ $arrGet = [];
if (isset($_GET)) {
foreach ($_GET as $key => $val) {
$arrGet[$key] = is_array($val) ? $val : strip_tags($val);
@@ -521,7 +566,7 @@ function appExpandTabs(act, key){
$output .= '$_POST :';
$output .= '';
- $arrPost = array();
+ $arrPost = [];
if (isset($_POST)) {
foreach ($_POST as $key => $val) {
$arrPost[$key] = is_array($val) ? $val : strip_tags($val);
@@ -534,7 +579,7 @@ function appExpandTabs(act, key){
$output .= '$_FILES :';
$output .= '';
- $arrFiles = array();
+ $arrFiles = [];
if (isset($_FILES)) {
foreach ($_FILES as $key => $val) {
$arrFiles[$key] = is_array($val) ? $val : strip_tags($val);
@@ -547,7 +592,7 @@ function appExpandTabs(act, key){
$output .= '$_COOKIE :';
$output .= '';
- $arrCookie = array();
+ $arrCookie = [];
if (isset($_COOKIE)) {
foreach ($_COOKIE as $key => $val) {
$arrCookie[$key] = is_array($val) ? $val : strip_tags($val);
@@ -560,7 +605,7 @@ function appExpandTabs(act, key){
$output .= '$_SESSION :';
$output .= '';
- $arrSession = array();
+ $arrSession = [];
if (isset($_SESSION)) {
foreach ($_SESSION as $key => $val) {
$arrSession[$key] = is_array($val) ? $val : strip_tags($val);
@@ -574,7 +619,7 @@ function appExpandTabs(act, key){
$output .= 'CONSTANTS :';
$output .= '';
$arrConstants = @get_defined_constants(true);
- $arrUserConstants = isset($arrConstants['user']) ? print_r($arrConstants['user'], true) : array();
+ $arrUserConstants = isset($arrConstants['user']) ? print_r($arrConstants['user'], true) : [];
$output .= $htmlCompression ? nl2br($arrUserConstants) : $arrUserConstants;
$output .= ' ';
$output .= ' ';
@@ -630,11 +675,16 @@ function appExpandTabs(act, key){
';
if ($totalQueries > 0) {
- $output .= A::t('core', 'SQL queries running time') . ': ' . $totalRunningTimeQueriesSql . ' sec.
';
+ $output .= A::t('core', 'SQL queries running time') . ': ' . $totalRunningTimeQueriesSql . ' sec.
Hide system queries ';
+ $output .= '
';
foreach (self::$_arrQueries as $msgKey => $msgVal) {
- $output .= $msgKey . ' ';
+ $dbugQuery = preg_match('/(show|truncate)/i', $msgKey);
+ $output .= '';
+ $output .= preg_replace('#]*>.*? #si', '', $msgKey) . ' ';
$output .= $msgVal[0] . ' ';
+ $output .= ' ';
}
+ $output .= ' ';
}
$output .= '
@@ -645,6 +695,8 @@ function appExpandTabs(act, key){
$output .= '';
} elseif ($debugBarState == 'middle') {
$output .= '';
+ } elseif ($debugBarState == 'close') {
+ $output .= '';
} else {
$output .= '';
}
diff --git a/framework/core/CModel.php b/framework/core/CModel.php
index 6ad0afe..ad92405 100644
--- a/framework/core/CModel.php
+++ b/framework/core/CModel.php
@@ -34,19 +34,20 @@ abstract class CModel
* Class constructor
* @param array $params
*/
- public function __construct($params = array())
+ public function __construct($params = [])
{
$this->_db = CDatabase::init($params);
$this->_error = CDatabase::getError();
$this->_errorMessage = CDatabase::getErrorMessage();
}
-
- /**
- * Initializes the database class
- * @param array $params
- */
- public static function init($params = array())
+
+ /**
+ * Initializes the database class
+ * @param array $params
+ * @return CModel|object
+ */
+ public static function init($params = [])
{
if (self::$_instance == null) self::$_instance = new self($params);
return self::$_instance;
diff --git a/framework/core/CRouter.php b/framework/core/CRouter.php
index e89eab4..4bb1d9b 100644
--- a/framework/core/CRouter.php
+++ b/framework/core/CRouter.php
@@ -69,7 +69,7 @@ class CRouter
/** @var string */
private $_module;
/** @var array */
- private static $_params = array();
+ private static $_params = [];
/**
diff --git a/framework/core/CView.php b/framework/core/CView.php
index a160bb6..5c6d73e 100644
--- a/framework/core/CView.php
+++ b/framework/core/CView.php
@@ -56,11 +56,11 @@ class CView
/** @var string */
private $_breadcrumbsTitle;
/** @var array */
- private $_vars = array();
+ private $_vars = [];
/** @var bool */
private $_isRendered = false;
/** @var array */
- private $_isCompRendered = array();
+ private $_isCompRendered = [];
/** @var boolean to enable html output compression */
private $_htmlCompression = false;
/** @var int */
@@ -339,12 +339,12 @@ public function render($params, $isPartial = false, $return = false)
}
}
- $this->_isRendered = true;
-
if ($return) {
return $output;
} else {
- echo $output;
+ $this->_isRendered = true;
+ $this->_renderHeaders();
+ echo $output;
}
///CDebug::addMessage('params', 'view', $this->__viewFile);
@@ -444,7 +444,7 @@ public function renderContent($view, $return = false)
* @throws Exception
* @return void
*/
- public function renderView($params, $data = array(), $return = false)
+ public function renderView($params, $data = [], $return = false)
{
try {
// Set default controller and action
@@ -645,5 +645,23 @@ public function isDefaultPage()
true :
false;
}
-
+
+ /**
+ * Render headers
+ */
+ private function _renderHeaders()
+ {
+ header('X-Author: ApPHP');
+
+ // Framework info headers
+ if (CConfig::get('httpHeaders.framework') === true) {
+ header('X-Framework-Name: ApPHP');
+ header('X-Framework-Version: '.A::version());
+ }
+
+ // Secure headers
+ if (CConfig::get('httpHeaders.secure') === true) {
+ CSecureHeaders::renderHeaders();
+ }
+ }
}
\ No newline at end of file
diff --git a/framework/core/interfaces.php b/framework/core/interfaces.php
index 740300e..caa1d25 100644
--- a/framework/core/interfaces.php
+++ b/framework/core/interfaces.php
@@ -21,3 +21,15 @@ interface IActiveRecord
*/
public static function model();
}
+
+
+/**
+ * IConsoleCommand is the interface that must be implemented by console command classes
+ */
+interface IConsoleCommand
+{
+ /**
+ * Handle specific console command
+ */
+ public static function handle();
+}
diff --git a/framework/db/CActiveRecord.php b/framework/db/CActiveRecord.php
index eaab3b9..d6d72ba 100644
--- a/framework/db/CActiveRecord.php
+++ b/framework/db/CActiveRecord.php
@@ -20,14 +20,15 @@
* __construct _relations _parentModel (static)
* __set _customFields _createObjectFromTable
* __get _encryptedFields _getRelations
- * __unset _beforeSave _getCustomFields
- * __callStatic _afterSave _addCustomFields
- * _beforeDelete _removeCustomFields
- * init (static) _afterDelete _prepareLimit
- * set _tableName
- * get _isEncryptedField
- * resultArray _getEncryptedFields
- * allowedColumns _getEncryptedField
+ * __isset _beforeSave _getCustomFields
+ * __unset _afterSave _addCustomFields
+ * __callStatic _beforeDelete _removeCustomFields
+ * _afterDelete _prepareLimit
+ * init (static) _tableName
+ * set _isEncryptedField
+ * get _getEncryptedFields
+ * resultArray _getEncryptedField
+ * allowedColumns
* isColumnExists
* setSpecialField
* getSpecialField
@@ -42,6 +43,8 @@
* getTranslations
* saveTranslations
*
+ * chunk
+ *
* find
* findByPk
* findByAttributes
@@ -74,1506 +77,1787 @@
class CActiveRecord extends CModel
{
- /** @var object */
- private static $_instance;
- /** @var string */
- private static $_className;
- /** @var Database */
- protected $_db;
- /** @var */
- protected $_dbDriver = '';
- /** @var */
- protected $_dbPrefix = '';
- /** @var boolean */
- protected $_error;
- /** @var string */
- protected $_errorMessage;
-
- /** @var string */
- protected $_table = '';
- /** @var string */
- protected $_tableTranslation = '';
- /** @var */
- protected $_columns = array();
- /** @var used to store fields from $_POST or another tables */
- protected $_specialFields = array();
- /** @var allowed fields */
- protected $_fillable = array();
- /** @var guarded fields */
- protected $_guarded = array();
- /** @var char */
- private $_backQuote = '`';
-
- /* class name => model */
- private static $_models = array();
-
- /** @var */
- private $_columnTypes = array();
- /** @var */
- private $_pkValue = 0;
- /** @var */
- private $_primaryKey;
- /** @var */
- private $_isNewRecord = false;
-
- /** @var */
- private static $_joinTypes = array(
- 'INNER JOIN',
- 'OUTER JOIN',
- 'LEFT JOIN',
- 'LEFT OUTER JOIN',
- 'RIGHT JOIN',
- 'RIGHT OUTER JOIN',
- 'JOIN',
- );
-
- const BELONGS_TO = 1; /* many-to-one */
- const HAS_ONE = 2; /* one-to-one */
- const HAS_MANY = 3; /* one-to-many */
- const MANY_MANY = 4; /* many-to-many */
-
- const INNER_JOIN = 'INNER JOIN';
- const OUTER_JOIN = 'OUTER JOIN';
- const LEFT_JOIN = 'LEFT JOIN';
- const LEFT_OUTER_JOIN = 'LEFT OUTER JOIN';
- const RIGHT_JOIN = 'RIGHT JOIN';
- const RIGHT_OUTER_JOIN = 'RIGHT OUTER JOIN';
- const JOIN = 'JOIN';
-
-
- /**
- * Class constructor
- * @param bool $createObject
- */
- public function __construct($createObject = true)
- {
- $this->_db = CDatabase::init();
-
- if ($createObject && !empty($this->_table)) {
- $this->_createObjectFromTable();
- $this->_pkValue = 0;
- }
-
- $this->_dbDriver = CConfig::get('db.driver');
- $this->_dbPrefix = CConfig::get('db.prefix');
-
- $this->_error = CDatabase::getError();
- $this->_errorMessage = CDatabase::getErrorMessage();
-
- // Set back quote according to database driver
- if (preg_match('/mssql|sqlsrv/i', $this->_dbDriver)) {
- $this->_backQuote = '';
- }
- }
-
- /**
- * Setter
- * @param string $index
- * @param mixed $value
- * @return void
- */
- public function __set($index, $value)
- {
- $this->_columns[$index] = $value;
- }
-
- /**
- * Getter
- * @param string $index
- * @return string
- */
- public function __get($index)
- {
- if (array_key_exists($index, $this->_columns)) {
- return $this->_columns[$index];
- } else {
- CDebug::AddMessage('errors', 'wrong_column' . $index, A::t('core', 'Wrong column name: {index} in table {table}', array('{index}' => $index, '{table}' => $this->_table)));
- return '';
- }
- }
-
- /**
- * Sets a active record property to be null
- * @param string $index
- * @return void
- */
- public function __unset($index)
- {
- if (array_key_exists($index, $this->_columns)) {
- unset($this->_columns[$index]);
- }
- }
-
- /**
- * Triggered when invoking inaccessible methods in an object context
- * We use this method to avoid calling model($className = __CLASS__) in derived class
- * @param string $method
- * @param array $args
- * @return mixed
- */
- public static function __callStatic($method, $args)
- {
- if (strtolower($method) == 'model') {
- if (count($args) == 1) {
- return self::_parentModel($args[0]);
- }
- }
- }
-
- /**
- * Initializes the database class
- * @param array $params
- */
- public static function init($params = array())
- {
- if (self::$_instance == null) self::$_instance = new self($params);
- return self::$_instance;
- }
-
- /**
- * Setter
- * @param string $index
- * @param mixed $value
- * @return void
- */
- public function set($index, $value)
- {
- $this->_columns[$index] = $value;
- }
-
- /**
- * Getter
- * @param string $index
- * @return string
- */
- public function get($index)
- {
- if (array_key_exists($index, $this->_columns)) {
- return $this->_columns[$index];
- } else {
- CDebug::AddMessage('errors', 'wrong_column' . $index, A::t('core', 'Wrong column name: {index} in table {table}', array('{index}' => $index, '{table}' => $this->_table)));
- return '';
- }
- }
-
- /**
- * Convert current object to array
- * @param bool $allowFilters Return only allowed fields
- * @return array
- */
- public function resultArray($allowFilters = false)
- {
- if (is_object($this)) {
- $columns = $this->_columns;
-
- if ($allowFilters) {
- // Validate fillable fields, left only allowed fields
- if (is_array($this->_fillable) && !empty($this->_fillable)) {
- $columns = array_intersect_key($columns, array_flip($this->_fillable));
- }
-
- // Validate guarded fields, exclude guarded fields
- if (is_array($this->_guarded) && !empty($this->_guarded)) {
- $columns = array_diff_key($columns, array_flip($this->_guarded));
- }
- }
-
- return $columns;
- }
- }
-
- /**
- * Return all allowed columns
- * @return array
- */
- public function allowedColumns()
- {
- return $this->resultArray(true);
- }
-
- /**
- * Checks if a given column exists
- * @param string $index
- * @return bool
- */
- public function isColumnExists($index)
- {
- return (array_key_exists($index, $this->_columns)) ? true : false;
- }
-
- /**
- * Setter for special fields
- * @param string $index
- * @param mixed $value
- */
- public function setSpecialField($index, $value)
- {
- $this->_specialFields[$index] = $value;
- }
-
- /**
- * Getter
- * @param string $index
- */
- public function getSpecialField($index)
- {
- return isset($this->_specialFields[$index]) ? $this->_specialFields[$index] : '';
- }
-
- /**
- * Get error status
- * @return boolean
- */
- public function getError()
- {
- return $this->_error;
- }
-
- /**
- * Get error message
- * @return string
- */
- public function getErrorMessage()
- {
- return $this->_errorMessage;
- }
-
- /**
- * Returns last query
- * @return string
- */
- public function lastQuery()
- {
- return $this->_db->lastQuery();
- }
-
- /**
- * Returns the primary key of the associated database table
- * @return string
- */
- public function primaryKey()
- {
- return $this->_primaryKey;
- }
-
- /**
- * Returns the primary key value
- * @return mixed
- */
- public function getPrimaryKey()
- {
- return $this->_pkValue;
- }
-
- /**
- * Returns the table name value
- * @param bool $usePrefix
- * @return string
- */
- public function getTableName($usePrefix = false)
- {
- return ($usePrefix ? $this->_dbPrefix : '') . $this->_table;
- }
-
- /**
- * Returns fields as array
- * @return array
- */
- public function getFieldsAsArray()
- {
- return $this->_columns;
- }
-
- /**
- * Returns if current operation is on new record or not
- * @return bool
- */
- public function isNewRecord()
- {
- return $this->_isNewRecord;
- }
-
- /**
- * Returns array of translation fields
- * @param array $params
- * @return array
- */
- public function getTranslations($params = array())
- {
- $key = isset($params['key']) ? $params['key'] : '';
- $value = isset($params['value']) ? $params['value'] : '';
- $fields = isset($params['fields']) ? $params['fields'] : array();
- $resultArray = array();
-
- if ($this->_tableTranslation == '') {
- CDebug::AddMessage('errors', 'get-translations', A::t('core', 'Property "{class}.{name}" is not defined.', array('{class}' => self::$_className, '{name}' => '_tableTranslation')));
- }
-
- $result = $this->_db->select(
- 'SELECT * FROM ' . $this->_tableName($this->_tableTranslation) . ' WHERE ' . $key . ' = :' . $key,
- array(':' . $key => $value)
- );
- foreach ($result as $res) {
- foreach ($fields as $field) {
- $resultArray[$res['language_code']][$field] = $res[$field];
- }
- }
-
- return $resultArray;
- }
-
- /**
- * Saves array of translation fields
- * @param array $params
- * @return array
- */
- public function saveTranslations($params = array())
- {
- $key = isset($params['key']) ? $params['key'] : '';
- $value = isset($params['value']) ? $params['value'] : '';
- $fields = isset($params['fields']) ? $params['fields'] : array();
- $paramsTranslation = array();
-
- foreach ($fields as $lang => $langInfo) {
- foreach ($langInfo as $langField => $langFieldValue) {
- $paramsTranslation[$langField] = $langFieldValue;
- }
- if ($this->isNewRecord()) {
- $paramsTranslation[$key] = $value;
- $paramsTranslation['language_code'] = $lang;
- $this->_db->insert($this->_tableTranslation, $paramsTranslation);
- } else {
- $this->_db->update($this->_tableTranslation, $paramsTranslation, $key . '=:key AND language_code=:language_code', array(':key' => $value, ':language_code' => $lang));
- }
- }
- }
-
-
- /*****************************************************************
- * ACTIVE RECORD METHODS
- *****************************************************************/
- /**
- * Returns the static model of the specified AR class
- * @param string $className
- *
- * EVERY derived AR class must define model() method in the following way,
- *
- * public static function model()
- * {
- * return parent::model(__CLASS__);
- * }
- *
- */
- private static function _parentModel($className = __CLASS__)
- {
- self::$_className = $className;
-
- if (isset(self::$_models[$className])) {
- return self::$_models[$className];
- } else {
- return self::$_models[$className] = new $className(null);
- }
- }
-
- /**
- * Create empty object from table
- * @return bool
- */
- private function _createObjectFromTable()
- {
- if (is_null($this->_table)) {
- return false;
- }
-
- $cols = $this->_db->showColumns($this->_table);
- if (!is_array($cols)) return false;
-
- switch ($this->_dbDriver) {
- case 'mssql':
- case 'sqlsrv':
- // Handle MSSQL or SQLSRV drivers
- $constraintStatement = "SELECT KU.TABLE_NAME, KU.COLUMN_NAME, KU.ORDINAL_POSITION, KU.CONSTRAINT_NAME
+ /** @var object */
+ private static $_instance;
+ /** @var string */
+ private static $_className;
+ /** @var Database */
+ protected $_db;
+ /** @var */
+ protected $_dbDriver = '';
+ /** @var */
+ protected $_dbPrefix = '';
+ /** @var boolean */
+ protected $_error;
+ /** @var string */
+ protected $_errorMessage;
+
+ /** @var string */
+ protected $_table = '';
+ /** @var string */
+ protected $_tableTranslation = '';
+ /** @var */
+ protected $_columns = [];
+ /** @var used to store fields from $_POST or another tables */
+ protected $_specialFields = [];
+ /** @var allowed fields */
+ protected $_fillable = [];
+ /** @var guarded fields */
+ protected $_guarded = [];
+ /** @var char */
+ private $_backQuote = '`';
+
+ /* class name => model */
+ private static $_models = [];
+
+ /** @var */
+ private $_columnTypes = [];
+ /** @var */
+ private $_pkValue = 0;
+ /** @var */
+ private $_primaryKey;
+ /** @var */
+ private $_isNewRecord = false;
+
+ /** @var */
+ private static $_joinTypes
+ = [
+ 'INNER JOIN',
+ 'OUTER JOIN',
+ 'LEFT JOIN',
+ 'LEFT OUTER JOIN',
+ 'RIGHT JOIN',
+ 'RIGHT OUTER JOIN',
+ 'JOIN',
+ ];
+
+ const BELONGS_TO = 1; /* many-to-one */
+ const HAS_ONE = 2; /* one-to-one */
+ const HAS_MANY = 3; /* one-to-many */
+ const MANY_MANY = 4; /* many-to-many */
+
+ const INNER_JOIN = 'INNER JOIN';
+ const OUTER_JOIN = 'OUTER JOIN';
+ const LEFT_JOIN = 'LEFT JOIN';
+ const LEFT_OUTER_JOIN = 'LEFT OUTER JOIN';
+ const RIGHT_JOIN = 'RIGHT JOIN';
+ const RIGHT_OUTER_JOIN = 'RIGHT OUTER JOIN';
+ const JOIN = 'JOIN';
+
+
+ /**
+ * Class constructor
+ *
+ * @param bool $createObject
+ */
+ public function __construct($createObject = true)
+ {
+ $this->_db = CDatabase::init();
+
+ if ($createObject && ! empty($this->_table)) {
+ $this->_createObjectFromTable();
+ $this->_pkValue = 0;
+ }
+
+ $this->_dbDriver = CConfig::get('db.driver');
+ $this->_dbPrefix = CConfig::get('db.prefix');
+
+ $this->_error = CDatabase::getError();
+ $this->_errorMessage = CDatabase::getErrorMessage();
+
+ // Set back quote according to database driver
+ if (preg_match('/mssql|sqlsrv/i', $this->_dbDriver)) {
+ $this->_backQuote = '';
+ }
+ }
+
+ /**
+ * Setter
+ *
+ * @param string $index
+ * @param mixed $value
+ *
+ * @return void
+ */
+ public function __set($index, $value)
+ {
+ $this->_columns[$index] = $value;
+ }
+
+ /**
+ * Getter
+ *
+ * @param string $index
+ *
+ * @return string
+ */
+ public function __get($index)
+ {
+ if (array_key_exists($index, $this->_columns)) {
+ return $this->_columns[$index];
+ } else {
+ CDebug::AddMessage(
+ 'errors',
+ 'wrong_column'.$index,
+ A::t(
+ 'core',
+ 'Wrong column name: {index} in table {table}',
+ ['{index}' => $index, '{table}' => $this->_table]
+ )
+ );
+
+ return '';
+ }
+ }
+
+ /**
+ * Checks if active record property exists
+ *
+ * @param string $index
+ *
+ * @return bool
+ */
+ public function __isset($index)
+ {
+ return array_key_exists($index, $this->_columns) ? true : false;
+ }
+
+ /**
+ * Sets a active record property to be null
+ *
+ * @param string $index
+ *
+ * @return void
+ */
+ public function __unset($index)
+ {
+ if (array_key_exists($index, $this->_columns)) {
+ unset($this->_columns[$index]);
+ }
+ }
+
+ /**
+ * Triggered when invoking inaccessible methods in an object context
+ * We use this method to avoid calling model($className = __CLASS__) in derived class
+ *
+ * @param string $method
+ * @param array $args
+ *
+ * @return mixed
+ */
+ public static function __callStatic($method, $args)
+ {
+ if (strtolower($method) == 'model') {
+ if (count($args) == 1) {
+ return self::_parentModel($args[0]);
+ }
+ }
+ }
+
+ /**
+ * Initializes the database class
+ *
+ * @param array $params
+ */
+ public static function init($params = [])
+ {
+ if (self::$_instance == null) {
+ self::$_instance = new self($params);
+ }
+
+ return self::$_instance;
+ }
+
+ /**
+ * Setter
+ *
+ * @param string $index
+ * @param mixed $value
+ *
+ * @return void
+ */
+ public function set($index, $value)
+ {
+ $this->_columns[$index] = $value;
+ }
+
+ /**
+ * Getter
+ *
+ * @param string $index
+ *
+ * @return string
+ */
+ public function get($index)
+ {
+ if (array_key_exists($index, $this->_columns)) {
+ return $this->_columns[$index];
+ } else {
+ CDebug::AddMessage(
+ 'errors',
+ 'wrong_column'.$index,
+ A::t(
+ 'core',
+ 'Wrong column name: {index} in table {table}',
+ ['{index}' => $index, '{table}' => $this->_table]
+ )
+ );
+
+ return '';
+ }
+ }
+
+ /**
+ * Convert current object to array
+ *
+ * @param bool $allowFilters Return only allowed fields
+ *
+ * @return array
+ */
+ public function resultArray($allowFilters = false)
+ {
+ if (is_object($this)) {
+ $columns = $this->_columns;
+
+ if ($allowFilters) {
+ // Validate fillable fields, left only allowed fields
+ if (is_array($this->_fillable) && ! empty($this->_fillable)) {
+ $columns = array_intersect_key($columns, array_flip($this->_fillable));
+ }
+
+ // Validate guarded fields, exclude guarded fields
+ if (is_array($this->_guarded) && ! empty($this->_guarded)) {
+ $columns = array_diff_key($columns, array_flip($this->_guarded));
+ }
+ }
+
+ return $columns;
+ }
+ }
+
+ /**
+ * Return all allowed columns
+ *
+ * @return array
+ */
+ public function allowedColumns()
+ {
+ return $this->resultArray(true);
+ }
+
+ /**
+ * Checks if a given column exists
+ *
+ * @param string $index
+ *
+ * @return bool
+ */
+ public function isColumnExists($index)
+ {
+ return (array_key_exists($index, $this->_columns)) ? true : false;
+ }
+
+ /**
+ * Setter for special fields
+ *
+ * @param string $index
+ * @param mixed $value
+ */
+ public function setSpecialField($index, $value)
+ {
+ $this->_specialFields[$index] = $value;
+ }
+
+ /**
+ * Getter
+ *
+ * @param string $index
+ */
+ public function getSpecialField($index)
+ {
+ return isset($this->_specialFields[$index]) ? $this->_specialFields[$index] : '';
+ }
+
+ /**
+ * Get error status
+ *
+ * @return boolean
+ */
+ public function getError()
+ {
+ return $this->_error;
+ }
+
+ /**
+ * Get error message
+ *
+ * @return string
+ */
+ public function getErrorMessage()
+ {
+ return $this->_errorMessage;
+ }
+
+ /**
+ * Returns last query
+ *
+ * @return string
+ */
+ public function lastQuery()
+ {
+ return $this->_db->lastQuery();
+ }
+
+ /**
+ * Returns the primary key of the associated database table
+ *
+ * @return string
+ */
+ public function primaryKey()
+ {
+ return $this->_primaryKey;
+ }
+
+ /**
+ * Returns the primary key value
+ *
+ * @return mixed
+ */
+ public function getPrimaryKey()
+ {
+ return $this->_pkValue;
+ }
+
+ /**
+ * Returns the table name value
+ *
+ * @param bool $usePrefix
+ *
+ * @return string
+ */
+ public function getTableName($usePrefix = false)
+ {
+ return ($usePrefix ? $this->_dbPrefix : '').$this->_table;
+ }
+
+ /**
+ * Returns fields as array
+ *
+ * @return array
+ */
+ public function getFieldsAsArray()
+ {
+ return $this->_columns;
+ }
+
+ /**
+ * Returns if current operation is on new record or not
+ *
+ * @return bool
+ */
+ public function isNewRecord()
+ {
+ return $this->_isNewRecord;
+ }
+
+ /**
+ * Returns array of translation fields
+ *
+ * @param array $params
+ *
+ * @return array
+ */
+ public function getTranslations($params = [])
+ {
+ $key = isset($params['key']) ? $params['key'] : '';
+ $value = isset($params['value']) ? $params['value'] : '';
+ $fields = isset($params['fields']) ? $params['fields'] : [];
+ $resultArray = [];
+
+ if ($this->_tableTranslation == '') {
+ CDebug::AddMessage(
+ 'errors',
+ 'get-translations',
+ A::t(
+ 'core',
+ 'Property "{class}.{name}" is not defined.',
+ ['{class}' => self::$_className, '{name}' => '_tableTranslation']
+ )
+ );
+ }
+
+ $result = $this->_db->select(
+ 'SELECT * FROM '.$this->_tableName($this->_tableTranslation).' WHERE '.$key.' = :'.$key,
+ [':'.$key => $value]
+ );
+ foreach ($result as $res) {
+ foreach ($fields as $field) {
+ $resultArray[$res['language_code']][$field] = $res[$field];
+ }
+ }
+
+ return $resultArray;
+ }
+
+ /**
+ * Saves array of translation fields
+ *
+ * @param array $params
+ *
+ * @return array
+ */
+ public function saveTranslations($params = [])
+ {
+ $key = isset($params['key']) ? $params['key'] : '';
+ $value = isset($params['value']) ? $params['value'] : '';
+ $fields = isset($params['fields']) ? $params['fields'] : [];
+ $paramsTranslation = [];
+
+ foreach ($fields as $lang => $langInfo) {
+ foreach ($langInfo as $langField => $langFieldValue) {
+ $paramsTranslation[$langField] = $langFieldValue;
+ }
+ if ($this->isNewRecord()) {
+ $paramsTranslation[$key] = $value;
+ $paramsTranslation['language_code'] = $lang;
+ $this->_db->insert($this->_tableTranslation, $paramsTranslation);
+ } else {
+ $this->_db->update(
+ $this->_tableTranslation,
+ $paramsTranslation,
+ $key.'=:key AND language_code=:language_code',
+ [':key' => $value, ':language_code' => $lang]
+ );
+ }
+ }
+ }
+
+
+ /*****************************************************************
+ * ACTIVE RECORD METHODS
+ *****************************************************************/
+ /**
+ * Returns the static model of the specified AR class
+ *
+ * @param string $className
+ *
+ * EVERY derived AR class must define model() method in the following way,
+ *
+ * public static function model()
+ * {
+ * return parent::model(__CLASS__);
+ * }
+ *
+ */
+ private static function _parentModel($className = __CLASS__)
+ {
+ self::$_className = $className;
+
+ if (isset(self::$_models[$className])) {
+ return self::$_models[$className];
+ } else {
+ return self::$_models[$className] = new $className(null);
+ }
+ }
+
+ /**
+ * Create empty object from table
+ *
+ * @return bool
+ */
+ private function _createObjectFromTable()
+ {
+ if (is_null($this->_table)) {
+ return false;
+ }
+
+ $cols = $this->_db->showColumns($this->_table);
+ if ( ! is_array($cols)) {
+ return false;
+ }
+
+ switch ($this->_dbDriver) {
+ case 'mssql':
+ case 'sqlsrv':
+ // Handle MSSQL or SQLSRV drivers
+ $constraintStatement = "SELECT KU.TABLE_NAME, KU.COLUMN_NAME, KU.ORDINAL_POSITION, KU.CONSTRAINT_NAME
FROM [INFORMATION_SCHEMA].[TABLE_CONSTRAINTS] TC, [INFORMATION_SCHEMA].[KEY_COLUMN_USAGE] KU
WHERE TC.TABLE_CATALOG = KU.TABLE_CATALOG AND
TC.CONSTRAINT_SCHEMA = KU.CONSTRAINT_SCHEMA AND
TC.CONSTRAINT_NAME = KU.CONSTRAINT_NAME AND
TC.CONSTRAINT_TYPE='PRIMARY KEY' AND
- LOWER(KU.TABLE_NAME)='" . strtolower($this->_table) . "' ";
-
- $primaryInfos = $this->_db->select($constraintStatement);
-
- $isPrimaryKey = false;
- foreach ($cols as $array) {
- // If NULL is allowed and NULL is default value, use null otherwise insert default value $array[4]
- // In mssql, sqlsrv the results contain are COLUMN_NAME, data_type, character_maximum_length
-
- $columnField = $array[0];
- $columnType = $array[1];
- $columnNullable = $array[2];
- $columnKey = $array[3];
- $columnDefault = $array[4];
- $columnExtra = $array[5];
-
- $isPrimaryKey = ($columnKey == 'PRI') ? true : false;
- if (!empty($primaryInfos)) {
- $found = false;
- foreach ($primaryInfos as $info) {
- if ((!$found) && (strcasecmp($info['COLUMN_NAME'], $columnField) == 0)) {
- $found = true;
- $isPrimaryKey = true;
- }
- }
- }
-
- if ($columnNullable === 'YES') {
- $this->_columns[$columnField] = null;
- } else {
- $this->_columns[$columnField] = ($columnDefault != '') ? $columnDefault : '';
- }
-
- $arrayParts = explode('(', $columnType);
- $this->_columnTypes[$columnField] = array_shift($arrayParts);
- if ($isPrimaryKey) {
- $this->_primaryKey = $columnField;
- }
- }
- break;
-
- default:
- // Handle all other db drivers
- // In mysql the results are Field, Type, Null, Key, Default, Extra
- foreach ($cols as $array) {
- // If NULL is allowed and NULL is default value, use null
- // otherwise insert default value $array[4]
- if ($array[2] === 'YES') {
- $this->_columns[$array[0]] = null;
- } else {
- $this->_columns[$array[0]] = ($array[4] != '') ? $array[4] : '';
- }
-
- $arrayParts = explode('(', $array[1]);
- $this->_columnTypes[$array[0]] = array_shift($arrayParts);
- if ($array[3] == 'PRI') {
- $this->_primaryKey = $array[0];
- }
- }
- break;
- }
-
- $this->_addCustomFields();
- if ($this->_primaryKey == '') $this->_primaryKey = 'id';
-
- return true;
- }
-
- /**
- * This method queries your database to find first related object
- * Ex.: find('postID = :postID AND isActive = :isActive', array(':postID'=>10, ':isActive'=>1));
- * Ex.: find(array('condition'=>'postID = :postID AND isActive = :isActive', 'order|orderBy'=>'id DESC'), 'params'=>array(':postID'=>10, ':isActive'=>1));
- * @param mixed $conditions
- * @param array $params
- * @param bool|string $cacheId
- * @return object
- */
- public function find($conditions = '', $params = array(), $cacheId = false)
- {
- if (is_array($conditions)) {
- $where = isset($conditions['condition']) ? $conditions['condition'] : '';
- if (isset($conditions['order'])) {
- $order = isset($conditions['order']) ? $conditions['order'] : '';
- } elseif (isset($conditions['orderBy'])) {
- $order = isset($conditions['orderBy']) ? $conditions['orderBy'] : '';
- }
- } else {
- $where = $conditions;
- $order = '';
- }
-
- $whereClause = !empty($where) ? ' WHERE ' . $where : '';
- $orderBy = !empty($order) ? ' ORDER BY ' . $order : '';
- $relations = $this->_getRelations();
- $customFields = $this->_getCustomFields();
- $encryptedField = $this->_getEncryptedFields();
- $limits = $this->_prepareLimit('1');
-
- $sql = 'SELECT
- ' . $limits['before'] . '
- ' . $this->_tableName() . '.*
- ' . $relations['fields'] . '
- ' . $customFields . '
- ' . $encryptedField . '
- FROM ' . $this->_tableName() . '
- ' . $relations['tables'] . '
- ' . $whereClause . '
- ' . $orderBy . '
- ' . $limits['after'];
-
- $result = $this->_db->select($sql, $params, 'fetchAll', PDO::FETCH_ASSOC, $cacheId);
- if (isset($result[0]) && is_array($result[0])) {
- foreach ($result[0] as $key => $val) {
- $this->$key = $val;
- if ($key == $this->_primaryKey) $this->_pkValue = $val;
- }
- return $this;
- } else {
- return null;
- }
- }
-
- /**
- * This method queries your database to find related objects by PK
- * Ex.: findByPk($pk, 'postID = :postID AND isActive = :isActive', array(':postID'=>10, ':isActive'=>1));
- * Ex.: findByPk($pk, array('condition'=>'postID = :postID AND isActive = :isActive', 'order|orderBy'=>'id DESC'), 'params'=>array(':postID'=>10, ':isActive'=>1));
- * @param string $pk
- * @param mixed $conditions
- * @param array $params
- * @param bool|string $cacheId
- * @return object
- */
- public function findByPk($pk, $conditions = '', $params = array(), $cacheId = false)
- {
- if (is_array($conditions)) {
- $where = isset($conditions['condition']) ? $conditions['condition'] : '';
- if (isset($conditions['order'])) {
- $order = isset($conditions['order']) ? $conditions['order'] : '';
- } elseif (isset($conditions['orderBy'])) {
- $order = isset($conditions['orderBy']) ? $conditions['orderBy'] : '';
- }
- } else {
- $where = $conditions;
- $order = '';
- }
-
- $whereClause = !empty($where) ? ' AND (' . $where . ')' : '';
- $orderBy = !empty($order) ? ' ORDER BY ' . $order : '';
- $relations = $this->_getRelations();
- $customFields = $this->_getCustomFields();
- $encryptedField = $this->_getEncryptedFields();
- $limits = $this->_prepareLimit('1');
-
- $sql = 'SELECT
- ' . $limits['before'] . '
- ' . $this->_tableName() . '.*
- ' . $relations['fields'] . '
- ' . $customFields . '
- ' . $encryptedField . '
- FROM ' . $this->_tableName() . '
- ' . $relations['tables'] . '
- WHERE ' . $this->_tableName() . '.' . $this->_primaryKey . ' = ' . (int)$pk . '
- ' . $whereClause . '
- ' . $orderBy . '
- ' . $limits['after'];
-
- $result = $this->_db->select($sql, $params, 'fetchAll', PDO::FETCH_ASSOC, $cacheId);
- if (isset($result[0]) && is_array($result[0])) {
- foreach ($result[0] as $key => $val) {
- $this->$key = $val;
- }
- $this->_pkValue = $pk;
- return $this;
- } else {
- return null;
- }
- }
-
- /**
- * This method queries your database to find related objects by attributes
- * Ex.: findByAttributes($attributes, 'postID = :postID AND isActive = :isActive', array(':postID'=>10, ':isActive'=>1));
- * Ex.: findByAttributes($attributes, array('condition'=>'postID = :postID AND isActive = :isActive', 'order|orderBy'=>'id DESC', 'limit'=>'0, 10'), 'params'=>array(':postID'=>10, ':isActive'=>1));
- * Ex.: $attributes = array('first_name'=>$firstName, 'last_name'=>$lastName);
- * @param array $attributes
- * @param mixed $conditions
- * @param array $params
- */
- public function findByAttributes($attributes, $conditions = '', $params = array())
- {
- if (is_array($conditions)) {
- $where = isset($conditions['condition']) ? $conditions['condition'] : '';
- if (isset($conditions['order'])) {
- $order = isset($conditions['order']) ? $conditions['order'] : '';
- } elseif (isset($conditions['orderBy'])) {
- $order = isset($conditions['orderBy']) ? $conditions['orderBy'] : '';
- }
- $limit = isset($conditions['limit']) ? $conditions['limit'] : '';
- } else {
- $where = $conditions;
- $order = '';
- $limit = '';
- }
-
- $whereClause = !empty($where) ? ' AND ' . $where : '';
- $orderBy = !empty($order) ? ' ORDER BY ' . $order : '';
- $limits = $this->_prepareLimit($limit);
-
- $relations = $this->_getRelations();
- $customFields = $this->_getCustomFields();
- $encryptedField = $this->_getEncryptedFields();
-
- $attributes_clause = '';
- foreach ($attributes as $key => $value) {
- $attributes_clause .= ' AND ' . $key . " = '" . $value . "'";
- }
-
- $sql = 'SELECT
- ' . $limits['before'] . '
- ' . $this->_tableName() . '.*
- ' . $relations['fields'] . '
- ' . $customFields . '
- FROM ' . $this->_tableName() . '
- ' . $relations['tables'] . '
- ' . $encryptedField . '
+ LOWER(KU.TABLE_NAME)='".strtolower($this->_table)."' ";
+
+ $primaryInfos = $this->_db->select($constraintStatement);
+
+ $isPrimaryKey = false;
+ foreach ($cols as $array) {
+ // If NULL is allowed and NULL is default value, use null otherwise insert default value $array[4]
+ // In mssql, sqlsrv the results contain are COLUMN_NAME, data_type, character_maximum_length
+
+ $columnField = $array[0];
+ $columnType = $array[1];
+ $columnNullable = $array[2];
+ $columnKey = $array[3];
+ $columnDefault = $array[4];
+ $columnExtra = $array[5];
+
+ $isPrimaryKey = ($columnKey == 'PRI') ? true : false;
+ if ( ! empty($primaryInfos)) {
+ $found = false;
+ foreach ($primaryInfos as $info) {
+ if (( ! $found) && (strcasecmp($info['COLUMN_NAME'], $columnField) == 0)) {
+ $found = true;
+ $isPrimaryKey = true;
+ }
+ }
+ }
+
+ if ($columnNullable === 'YES') {
+ $this->_columns[$columnField] = null;
+ } else {
+ $this->_columns[$columnField] = ($columnDefault != '') ? $columnDefault : '';
+ }
+
+ $arrayParts = explode('(', $columnType);
+ $this->_columnTypes[$columnField] = array_shift($arrayParts);
+ if ($isPrimaryKey) {
+ $this->_primaryKey = $columnField;
+ }
+ }
+ break;
+
+ default:
+ // Handle all other db drivers
+ // In mysql the results are Field, Type, Null, Key, Default, Extra
+ foreach ($cols as $array) {
+ // If NULL is allowed and NULL is default value, use null
+ // otherwise insert default value $array[4]
+ if ($array[2] === 'YES') {
+ $this->_columns[$array[0]] = null;
+ } else {
+ $this->_columns[$array[0]] = ($array[4] != '') ? $array[4] : '';
+ }
+
+ $arrayParts = explode('(', $array[1]);
+ $this->_columnTypes[$array[0]] = array_shift($arrayParts);
+ if ($array[3] == 'PRI') {
+ $this->_primaryKey = $array[0];
+ }
+ }
+ break;
+ }
+
+ $this->_addCustomFields();
+ if ($this->_primaryKey == '') {
+ $this->_primaryKey = 'id';
+ }
+
+ return true;
+ }
+
+ /**
+ * Split AR result into parts (chunks)
+ * Ex.: chunk(['condition'=>'post_id = :postID AND is_active = :isActive', 'select'=>'', 'group|groupBy'=>'', 'order|orderBy'=>'id DESC', 'limit'=>'0, 10'), [':postID'=>10, ':isActive'=>1]];
+ * Ex.: chunk(CConfig::get('db.prefix').$this->_tableTranslation.'.news_text LIKE :keywords', [':keywords'=>'%'.$keywords.'%']);
+ *
+ * @param array $conditions
+ * @param array $params
+ * @param int $size
+ * @param callable $callback
+ *
+ * @return null
+ */
+ public function chunk(array $conditions = [], array $params = [], int $size = 0, callable $callback = null)
+ {
+ if (is_int($size) && $size > 0 && ! empty($callback)) {
+ if ( ! isset($conditions['limit'])) {
+ $from = 0;
+ $limitSize = $size;
+ } else {
+ $limitParts = explode(',', $conditions['limit']);
+ $from = isset($limitParts[0]) ? $limitParts[0] : 0;
+ $limitSize = isset($limitParts[1]) ? $limitParts[1] : $size;
+ if ($size >= $limitSize) {
+ $size = $limitSize;
+ }
+ }
+
+ $conditions['limit'] = "$from, $size";
+ $count = 0;
+
+ while ($result = $this->findAll($conditions, $params)) {
+ $callback($result);
+ $from += $size;
+ $conditions['limit'] = "$from, $size";
+
+ $count += $size;
+ if ($count >= $limitSize) {
+ break;
+ }
+ }
+ } else {
+ CDebug::AddMessage(
+ 'errors',
+ 'chunk',
+ A::t(
+ 'core',
+ 'Wrong params for chunk size: {size} or callback method is callable.',
+ ['{size}' => $size]
+ )
+ );
+ }
+
+ return null;
+ }
+
+ /**
+ * This method queries your database to find first related object
+ * Ex.: find('postID = :postID AND isActive = :isActive', [':postID'=>10, ':isActive'=>1]);
+ * Ex.: find(['condition'=>'postID = :postID AND isActive = :isActive', 'order|orderBy'=>'id DESC'], [':postID'=>10, ':isActive'=>1]);
+ *
+ * @param mixed $conditions
+ * @param array $params
+ * @param bool|string $cacheId
+ *
+ * @return object
+ */
+ public function find($conditions = '', $params = [], $cacheId = false)
+ {
+ if (is_array($conditions)) {
+ $where = isset($conditions['condition']) ? $conditions['condition'] : '';
+ if (isset($conditions['order'])) {
+ $order = isset($conditions['order']) ? $conditions['order'] : '';
+ } elseif (isset($conditions['orderBy'])) {
+ $order = isset($conditions['orderBy']) ? $conditions['orderBy'] : '';
+ }
+ } else {
+ $where = $conditions;
+ $order = '';
+ }
+
+ $whereClause = ! empty($where) ? ' WHERE '.$where : '';
+ $orderBy = ! empty($order) ? ' ORDER BY '.$order : '';
+ $relations = $this->_getRelations();
+ $customFields = $this->_getCustomFields();
+ $encryptedField = $this->_getEncryptedFields();
+ $limits = $this->_prepareLimit('1');
+
+ $sql = 'SELECT
+ '.$limits['before'].'
+ '.$this->_tableName().'.*
+ '.$relations['fields'].'
+ '.$customFields.'
+ '.$encryptedField.'
+ FROM '.$this->_tableName().'
+ '.$relations['tables'].'
+ '.$whereClause.'
+ '.$orderBy.'
+ '.$limits['after'];
+
+ $result = $this->_db->select($sql, $params, 'fetchAll', PDO::FETCH_ASSOC, $cacheId);
+ if (isset($result[0]) && is_array($result[0])) {
+ foreach ($result[0] as $key => $val) {
+ $this->$key = $val;
+ if ($key == $this->_primaryKey) {
+ $this->_pkValue = $val;
+ }
+ }
+
+ return $this;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * This method queries your database to find related objects by PK
+ * Ex.: findByPk($pk, 'postID = :postID AND isActive = :isActive', array(':postID'=>10, ':isActive'=>1));
+ * Ex.: findByPk($pk, array('condition'=>'postID = :postID AND isActive = :isActive', 'order|orderBy'=>'id DESC'), array(':postID'=>10, ':isActive'=>1));
+ *
+ * @param string $pk
+ * @param mixed $conditions
+ * @param array $params
+ * @param bool|string $cacheId
+ *
+ * @return object
+ */
+ public function findByPk($pk, $conditions = '', $params = [], $cacheId = false)
+ {
+ if (is_array($conditions)) {
+ $where = isset($conditions['condition']) ? $conditions['condition'] : '';
+ if (isset($conditions['order'])) {
+ $order = isset($conditions['order']) ? $conditions['order'] : '';
+ } elseif (isset($conditions['orderBy'])) {
+ $order = isset($conditions['orderBy']) ? $conditions['orderBy'] : '';
+ }
+ } else {
+ $where = $conditions;
+ $order = '';
+ }
+
+ $whereClause = ! empty($where) ? ' AND ('.$where.')' : '';
+ $orderBy = ! empty($order) ? ' ORDER BY '.$order : '';
+ $relations = $this->_getRelations();
+ $customFields = $this->_getCustomFields();
+ $encryptedField = $this->_getEncryptedFields();
+ $limits = $this->_prepareLimit('1');
+
+ $sql = 'SELECT
+ '.$limits['before'].'
+ '.$this->_tableName().'.*
+ '.$relations['fields'].'
+ '.$customFields.'
+ '.$encryptedField.'
+ FROM '.$this->_tableName().'
+ '.$relations['tables'].'
+ WHERE '.$this->_tableName().'.'.$this->_primaryKey.' = '.(int)$pk.'
+ '.$whereClause.'
+ '.$orderBy.'
+ '.$limits['after'];
+
+ $result = $this->_db->select($sql, $params, 'fetchAll', PDO::FETCH_ASSOC, $cacheId);
+ if (isset($result[0]) && is_array($result[0])) {
+ foreach ($result[0] as $key => $val) {
+ $this->$key = $val;
+ }
+ $this->_pkValue = $pk;
+
+ return $this;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * This method queries your database to find related objects by attributes
+ * Ex.: findByAttributes($attributes, 'postID = :postID AND isActive = :isActive', array(':postID'=>10, ':isActive'=>1));
+ * Ex.: findByAttributes($attributes, array('condition'=>'postID = :postID AND isActive = :isActive', 'order|orderBy'=>'id DESC', 'limit'=>'0, 10'), array(':postID'=>10, ':isActive'=>1));
+ * Ex.: $attributes = array('first_name'=>$firstName, 'last_name'=>$lastName);
+ *
+ * @param array $attributes
+ * @param mixed $conditions
+ * @param array $params
+ *
+ * @return mixed
+ */
+ public function findByAttributes($attributes, $conditions = '', $params = [])
+ {
+ if (is_array($conditions)) {
+ $where = isset($conditions['condition']) ? $conditions['condition'] : '';
+ if (isset($conditions['order'])) {
+ $order = isset($conditions['order']) ? $conditions['order'] : '';
+ } elseif (isset($conditions['orderBy'])) {
+ $order = isset($conditions['orderBy']) ? $conditions['orderBy'] : '';
+ }
+ $limit = isset($conditions['limit']) ? $conditions['limit'] : '';
+ } else {
+ $where = $conditions;
+ $order = '';
+ $limit = '';
+ }
+
+ $whereClause = ! empty($where) ? ' AND '.$where : '';
+ $orderBy = ! empty($order) ? ' ORDER BY '.$order : '';
+ $limits = $this->_prepareLimit($limit);
+
+ $relations = $this->_getRelations();
+ $customFields = $this->_getCustomFields();
+ $encryptedField = $this->_getEncryptedFields();
+
+ $attributes_clause = '';
+ foreach ($attributes as $key => $value) {
+ $attributes_clause .= ' AND '.$key." = '".$value."'";
+ }
+
+ $sql = 'SELECT
+ '.$limits['before'].'
+ '.$this->_tableName().'.*
+ '.$relations['fields'].'
+ '.$customFields.'
+ FROM '.$this->_tableName().'
+ '.$relations['tables'].'
+ '.$encryptedField.'
WHERE 1 = 1
- ' . $attributes_clause . '
- ' . $whereClause . '
- ' . $orderBy . '
- ' . $limits['after'];
-
- return $this->_db->select($sql, $params);
- }
-
- /**
- * This method queries your database to find all related objects
- * Ex.: findAll('post_id = :postID AND is_active = :isActive', array(':postID'=>10, ':isActive'=>1));
- * Ex.: findAll(array('condition'=>'post_id = :postID AND is_active = :isActive', 'select'=>'', 'group|groupBy'=>'', 'order|orderBy'=>'id DESC', 'limit'=>'0, 10'), array(':postID'=>10, ':isActive'=>1));
- * Ex.: findAll(CConfig::get('db.prefix').$this->_tableTranslation.'.news_text LIKE :keywords', array(':keywords'=>'%'.$keywords.'%'));
- * @param mixed $conditions
- * @param array $params 'select': MAX(date), name or CConfig::get('db.prefix').table.field_name etc. - actually for ONLY_FULL_GROUP_BY mode
- * 'groupBy': table.field_name or field_name
- * @param bool|string $cacheId
- * @param int $fetchMode
- * @return array
- */
- public function findAll($conditions = '', $params = array(), $cacheId = false, $fetchMode = PDO::FETCH_ASSOC)
- {
- if (is_array($conditions)) {
- $where = isset($conditions['condition']) ? $conditions['condition'] : '';
- if (isset($conditions['group'])) {
- $group = isset($conditions['group']) ? $conditions['group'] : '';
- } elseif (isset($conditions['groupBy'])) {
- $group = isset($conditions['groupBy']) ? $conditions['groupBy'] : '';
- }
- if (isset($conditions['order'])) {
- $order = isset($conditions['order']) ? $conditions['order'] : '';
- } elseif (isset($conditions['orderBy'])) {
- $order = isset($conditions['orderBy']) ? $conditions['orderBy'] : '';
- }
- $limit = isset($conditions['limit']) ? $conditions['limit'] : '';
- $select = isset($conditions['select']) ? $conditions['select'] : '';
- } else {
- $where = $conditions;
- $group = '';
- $order = '';
- $limit = '';
- $select = '';
- }
-
- $whereClause = !empty($where) ? ' WHERE ' . $where : '';
- $groupBy = !empty($group) ? ' GROUP BY ' . $group : '';
- $orderBy = !empty($order) ? ' ORDER BY ' . $order : '';
- $limits = $this->_prepareLimit($limit);
- $selectList = !empty($select) ? $select : $this->_tableName() . '.*';
-
- $relations = $this->_getRelations();
- $customFields = $this->_getCustomFields();
- $encryptedField = $this->_getEncryptedFields();
-
- $sql = 'SELECT
- ' . $limits['before'] . '
- ' . $selectList . '
- ' . $relations['fields'] . '
- ' . $customFields . '
- ' . $encryptedField . '
- FROM ' . $this->_tableName() . '
- ' . $relations['tables'] . '
- ' . $whereClause . '
- ' . $groupBy . '
- ' . $orderBy . '
- ' . $limits['after'];
-
- return $this->_db->select($sql, $params, 'fetchAll', $fetchMode, $cacheId);
- }
-
- /**
- * This method queries your database to find first related record primary key
- * Ex.: findPk('postID = :postID AND isActive = :isActive', array(':postID'=>10, ':isActive'=>1));
- * @param mixed $conditions
- * @param array $params
- * @return int
- */
- public function findPk($conditions = '', $params = array())
- {
- if ($result = $this->find($conditions, $params)) {
- $key = $this->_primaryKey;
- return $result->$key;
- }
-
- return '';
- }
-
- /**
- * Create new record
- * @param array $data
- * @param bool $preOperations
- * @return bool
- */
- public function create($data = array(), $preOperations = true)
- {
- $allowOperation = true;
- if ($preOperations) {
- if (!$this->_beforeSave($this->_pkValue)) {
- $allowOperation = false;
- CDebug::AddMessage('errors', 'before-save', A::t('core', 'AR before operation on table: {table}', array('{table}' => $this->_table)));
- }
- }
-
- if ($allowOperation) {
- $result = $this->_db->insert($this->_table, $data);
- $this->_isNewRecord = true;
- // Save last inset ID
- $this->_pkValue = (int)$result;
-
- if ($result) {
- if ($preOperations) {
- $this->_afterSave($this->_pkValue);
- }
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Update existing record
- * @param int $id
- * @param array $data
- * @param bool $preOperations
- * @param bool $forceSave
- * @return boolean
- */
- public function update($id, $data = array(), $preOperations = true, $forceSave = false)
- {
- $allowOperation = true;
- if ($preOperations) {
- if (!$this->_beforeSave($id)) {
- $allowOperation = false;
- CDebug::AddMessage('errors', 'before-save', A::t('core', 'AR before operation on table: {table}', array('{table}' => $this->_table)));
- }
- }
-
- if ($allowOperation) {
- $result = $this->_db->update($this->_table, $data, $this->_primaryKey . ' = :primary_key', array(':primary_key' => (int)$id), $forceSave);
- $this->_isNewRecord = false;
-
- if ($result) {
- if ($preOperations) {
- $this->_afterSave($id);
- }
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Save data
- * @param CRecordEntity $entity
- * @param bool $forceSave
- * @return boolean
- */
- public function save($entity = null, $forceSave = false)
- {
- $data = array();
- $this->_removeCustomFields();
-
- if (!is_null($entity) && ($entity instanceof CRecordEntity)) {
- // ---------------------------------------
- // Handle Entity
- // ---------------------------------------
- $columns = $entity->allowedColumns();
- $primaryKey = $entity->primaryKey();
- $pkValue = $entity->getPrimaryKey();
-
- foreach ($columns as $column => $val) {
- if ($column != 'id' && $column != $primaryKey) {
- $data[$column] = $entity->$column;
- }
- }
-
- if ($pkValue > 0) {
- $result = $this->_db->update($this->_table, $data, $primaryKey . ' = :primary_key', array(':primary_key' => (int)$pkValue), $forceSave);
- } else {
- $result = $this->_db->insert($this->_table, $data, $forceSave);
- // Save last inset ID
- $pkValue = (int)$result;
- }
-
- if ($result) {
- $this->_afterSave($pkValue);
- return true;
- }
- } else {
- // ---------------------------------------
- // Handle Model
- // ---------------------------------------
- if ($this->_beforeSave($this->_pkValue)) {
- $columns = $this->allowedColumns();
- foreach ($columns as $column => $val) {
- $relations = $this->_getRelations();
- if ($column != 'id' && $column != $this->_primaryKey && !in_array($column, $relations['fieldsArray'])) { // && ($column != 'created_at') && !$NEW)
- //$value = $this->$column;
- //if(array_search($this->_columnTypes[$column], array('int', 'float', 'decimal'))){
- // $value = filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
- //}
- if ($this->_isEncryptedField($column)) {
- $encryptedField = $this->_getEncryptedField($column);
- $data[$column] = array('param_key' => $encryptedField['encrypt'] . '(' . $column . ',"' . $encryptedField['key'] . '")', 'param_value' => $this->$column);
- } else {
- $data[$column] = $this->$column;
- }
- }
- }
-
- if ($this->_pkValue > 0) {
- $result = $this->_db->update($this->_table, $data, $this->_primaryKey . ' = :primary_key', array(':primary_key' => (int)$this->_pkValue), $forceSave);
- $this->_isNewRecord = false;
- } else {
- $result = $this->_db->insert($this->_table, $data, $forceSave);
- $this->_isNewRecord = true;
- // Save last inset ID
- $this->_pkValue = (int)$result;
- }
-
- if ($result) {
- $this->_afterSave($this->_pkValue);
- return true;
- }
- } else {
- CDebug::AddMessage('errors', 'before-save', A::t('core', 'AR before operation on table: {table}', array('{table}' => $this->_table)));
- }
- }
-
- return false;
- }
-
- /**
- * Clear primary key
- * @return boolean
- */
- public function clearPkValue()
- {
- $this->_pkValue = 0;
-
- return true;
- }
-
- /**
- * Reset the object with fields
- * @return boolean
- */
- public function reset()
- {
- $this->_columns = array();
- $this->_specialFields = array();
-
- if (!empty($this->_table)) {
- $this->_createObjectFromTable();
- $this->_pkValue = 0;
- }
-
- return true;
- }
-
- /**
- * Updates records with the specified primary key
- * See {@link find()} for detailed explanation about $conditions
- * Ex.: updateByPk($pk, array('name'=>$value), 'postID = 10 AND isActive = 1');
- * @param string $pk
- * @param array $data
- * @param mixed $conditions
- * @param array $params
- * @return bool
- */
- public function updateByPk($pk, $data = array(), $conditions = '', $params = array())
- {
- if ($this->_beforeSave($pk)) {
- if (is_array($conditions)) {
- $where = isset($conditions['condition']) ? $conditions['condition'] : '';
- } else {
- $where = $conditions;
- }
- $whereClause = !empty($where) ? ' AND ' . $where : '';
- $params[':primary_key'] = (int)$pk;
-
- $result = $this->_db->update($this->_table, $data, $this->_primaryKey . ' = :primary_key' . $whereClause, $params);
- if ($result) {
- $this->_afterSave($pk);
- return true;
- } else {
- return false;
- }
- } else {
- CDebug::AddMessage('errors', 'before-update', A::t('core', 'AR before operation on table: {table}', array('{table}' => $this->_table)));
- }
- }
-
- /**
- * Updates the rows matching the specified condition
- * Ex.: updateAll(array('name'=>$value), 'postID = 10 AND isActive = 1');
- * Ex.: updateAll(array('name'=>$value), 'postID = 10 AND isActive = :isActive', array(':isActiv'=>1));
- * @param array $data
- * @param mixed $conditions
- * @param array $params
- * @return bool
- */
- public function updateAll($data = array(), $conditions = '', $params = array())
- {
- if (is_array($conditions)) {
- $where = isset($conditions['condition']) ? $conditions['condition'] : '';
- } else {
- $where = $conditions;
- }
- $whereClause = !empty($where) ? $where : '1';
-
- $result = $this->_db->update($this->_table, $data, $whereClause, $params);
- if ($result) {
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Remove the row from database if AR instance has been populated with this row
- * Ex.: $post = PostModel::model()->findByPk(10);
- * $post->delete();
- * @return boolean
- */
- public function delete()
- {
- if (!empty($this->_pkValue) && $this->deleteByPk($this->_pkValue)) {
- return true;
- }
-
- return false;
- }
-
- /**
- * Remove the rows matching the specified condition and primary key(s)
- * Ex.: deleteByPk(10, 'postID = :postID AND isActive = :isActive', array(':postID'=>10, ':isActive'=>1));
- * @param string $pk
- * @param mixed $conditions
- * @param array $params
- * @return boolean
- */
- public function deleteByPk($pk, $conditions = '', $params = array())
- {
- if ($this->_beforeDelete($pk)) {
- if (is_array($conditions)) {
- $where = isset($conditions['condition']) ? $conditions['condition'] : '';
- } else {
- $where = $conditions;
- }
- $whereClause = !empty($where) ? ' WHERE ' . $where : '';
- $params[':primary_key'] = (int)$pk;
-
- $result = $this->_db->delete($this->_table, $this->_primaryKey . ' = :primary_key' . $whereClause, $params);
- if ($result) {
- $this->_afterDelete($pk);
- return true;
- }
- } else {
- CDebug::AddMessage('errors', 'before-delete', A::t('core', 'AR before delete operation on table: {table}', array('{table}' => $this->_table)));
- }
-
- return false;
- }
-
- /**
- * Remove the rows matching the specified condition
- * Ex.: deleteAll('postID = :postID AND isActive = :isActive', array(':postID'=>10, ':isActive'=>1));
- * Ex.: deleteAll(array('condition'=>'postID = :postID AND isActive = :isActive'), array(':postID'=>10, ':isActive'=>1));
- * @param mixed $conditions
- * @param array $params
- * @return boolean
- */
- public function deleteAll($conditions = '', $params = array())
- {
- if ($this->_beforeDelete()) {
- if (is_array($conditions)) {
- $where = isset($conditions['condition']) ? $conditions['condition'] : '';
- } else {
- $where = $conditions;
- }
- $whereClause = !empty($where) ? ' WHERE ' . $where : '';
-
- $result = $this->_db->delete($this->_table, $whereClause, $params);
- if ($result) {
- $this->_afterDelete();
- return true;
- }
- } else {
- CDebug::AddMessage('errors', 'before-delete', A::t('core', 'AR before delete operation on table: {table}', array('{table}' => $this->_table)));
- }
-
- return false;
- }
-
- /**
- * This method selects distinct value
- * @param string $field
- * @return array
- */
- public function distinct($field = '')
- {
- return $this->findAll(array('group' => $this->_tableName() . '.' . $field));
- }
-
- /**
- * This method reloads model data according to the current primary key
- * @return object
- */
- public function refresh()
- {
- return $this->findByPk($this->_pkValue);
- }
-
- /**
- * This method check if there is at least one row satisfying the specified condition
- * Ex.: exists('postID = :postID AND isActive = :isActive', array(':postID'=>10, ':isActive'=>1));
- * @param mixed $conditions
- * @param array $params
- * @return bolean
- */
- public function exists($conditions = '', $params = array())
- {
- if (is_array($conditions)) {
- $where = isset($conditions['condition']) ? $conditions['condition'] : '';
- } else {
- $where = $conditions;
- }
-
- $whereClause = !empty($where) ? ' WHERE ' . $where : '';
- $limits = $this->_prepareLimit('1');
-
- $sql = 'SELECT
- ' . $limits['before'] . '
- ' . $this->_tableName() . '.*
- FROM ' . $this->_tableName() . '
- ' . $whereClause . '
- ' . $limits['after'];
-
- $result = $this->_db->select($sql, $params);
-
- return ($result) ? true : false;
- }
-
- /**
- * Finds the number of rows satisfying the specified query condition
- * Ex.: count('postID = :postID AND isActive = :isActive', array(':postID'=>10, ':isActive'=>1));
- * Ex.: count(array('condition'=>'post_id = :postID AND is_active = :isActive', 'select'=>'', 'count'=>'*', 'group|groupBy'=>'', 'order|orderBy'=>'', 'allRows'=>false), array(':postID'=>10, ':isActive'=>1));
- * @param mixed $conditions
- * @param array $params
- * @return integer
- */
- public function count($conditions = '', $params = array())
- {
- if (is_array($conditions)) {
- $where = isset($conditions['condition']) ? $conditions['condition'] : '';
- if (isset($conditions['group'])) {
- $group = isset($conditions['group']) ? $conditions['group'] : '';
- } elseif (isset($conditions['groupBy'])) {
- $group = isset($conditions['groupBy']) ? $conditions['groupBy'] : '';
- }
- if (!empty($group)) {
- if (isset($conditions['order'])) {
- $order = isset($conditions['order']) ? $conditions['order'] : '';
- } elseif (isset($conditions['orderBy'])) {
- $order = isset($conditions['orderBy']) ? $conditions['orderBy'] : '';
- }
- }
- $count = isset($conditions['count']) ? $conditions['count'] : '*';
- $select = isset($conditions['select']) ? $conditions['select'] : '';
- $allRows = isset($conditions['allRows']) ? (bool)$conditions['allRows'] : false;
- } else {
- $where = $conditions;
- $group = '';
- $order = '';
- $count = '*';
- $select = '';
- $allRows = false;
- }
-
- $whereClause = !empty($where) ? ' WHERE ' . $where : '';
- $groupBy = !empty($group) ? ' GROUP BY ' . $group : '';
- $orderBy = !empty($order) ? ' ORDER BY ' . $order : '';
- $limits = $this->_prepareLimit(($allRows ? '' : '1'));
- $relations = $this->_getRelations();
-
- $sql = 'SELECT
- ' . $limits['before'] . '
- COUNT(' . $count . ') as cnt
- ' . ($select ? ', ' . $select : '') . '
- FROM ' . $this->_tableName() . '
- ' . $relations['tables'] . '
- ' . $whereClause . '
- ' . $groupBy . '
- ' . $orderBy . '
- ' . $limits['after'];
-
- $result = $this->_db->select($sql, $params);
-
- if ($allRows) {
- return (isset($result)) ? $result : null;
- } else {
- return (isset($result[0]['cnt'])) ? $result[0]['cnt'] : 0;
- }
- }
-
- /**
- * Finds a maximum value of the specified column
- * Ex.: max('id', 'postID = :postID AND isActive = :isActive', array(':postID'=>10, ':isActive'=>1));
- * @param string $column
- * @param mixed $conditions
- * @param array $params
- * @return integer
- */
- public function max($column = '', $conditions = '', $params = array())
- {
- if (is_array($conditions)) {
- $where = isset($conditions['condition']) ? $conditions['condition'] : '';
- } else {
- $where = $conditions;
- }
-
- $whereClause = !empty($where) ? ' WHERE ' . $where : '';
- $column = !empty($column) ? $this->_tableName() . '.' . $column : $this->_primaryKey;
- $relations = $this->_getRelations();
- $limits = $this->_prepareLimit('1');
-
- $sql = 'SELECT
- ' . $limits['before'] . '
- MAX(' . $column . ') as column_max
- FROM ' . $this->_tableName() . '
- ' . $relations['tables'] . '
- ' . $whereClause . '
- ' . $limits['after'];
-
- $result = $this->_db->select($sql, $params);
-
- return (isset($result[0]['column_max'])) ? $result[0]['column_max'] : 0;
- }
-
- /**
- * Finds a minimum value of the specified column
- * Ex.: min('id', 'postID = :postID AND isActive = :isActive', array(':postID'=>10, 'isActive'=>1));
- * @param string $column
- * @param mixed $conditions
- * @param array $params
- * @return integer
- */
- public function min($column = '', $conditions = '', $params = array())
- {
- if (is_array($conditions)) {
- $where = isset($conditions['condition']) ? $conditions['condition'] : '';
- } else {
- $where = $conditions;
- }
-
- $whereClause = !empty($where) ? ' WHERE ' . $where : '';
- $column = !empty($column) ? $this->_tableName() . '.' . $column : $this->_primaryKey;
- $relations = $this->_getRelations();
- $limits = $this->_prepareLimit('1');
-
- $sql = 'SELECT
- ' . $limits['before'] . '
- MIN(' . $column . ') as column_min
- FROM ' . $this->_tableName() . '
- ' . $relations['tables'] . '
- ' . $whereClause . '
- ' . $limits['after'];
-
- $result = $this->_db->select($sql, $params);
-
- return (isset($result[0]['column_min'])) ? $result[0]['column_min'] : 0;
- }
-
- /**
- * Finds a sum value of the specified column
- * Ex.: sum('id', 'postID = :postID AND isActive = :isActive', array(':postID'=>10, ':isActive'=>1));
- * @param string $column
- * @param mixed $conditions
- * @param array $params
- * @return integer
- */
- public function sum($column = '', $conditions = '', $params = array())
- {
- if (is_array($conditions)) {
- $where = isset($conditions['condition']) ? $conditions['condition'] : '';
- } else {
- $where = $conditions;
- }
-
- $whereClause = !empty($where) ? ' WHERE ' . $where : '';
- $column = !empty($column) ? $column : '';
- $relations = $this->_getRelations();
- $limits = $this->_prepareLimit('1');
-
- $sql = 'SELECT
- ' . $limits['before'] . '
- SUM(' . $column . ') as column_sum
- FROM ' . $this->_tableName() . '
- ' . $relations['tables'] . '
- ' . $whereClause . '
- ' . $limits['after'];
-
- $result = $this->_db->select($sql, $params);
-
- return (isset($result[0]['column_sum'])) ? $result[0]['column_sum'] : 0;
- }
-
- /**
- * Used to define relations between different tables in database and current $_table
- * This method should be overridden
- */
- protected function _relations()
- {
- return array();
- }
-
- /**
- * Used to define custom fields
- * This method should be overridden
- * Usage: 'CONCAT('.CConfig::get('db.prefix').$this->_table.'.last_name, " ", '.CConfig::get('db.prefix').$this->_table.'.first_name)' => 'fullname'
- * '(SELECT COUNT(*) FROM '.CConfig::get('db.prefix').$this->_tableTranslation.')' => 'records_count'
- */
- protected function _customFields()
- {
- return array();
- }
-
- /**
- * Used to define encrypted fields
- * This method should be overridden
- * Usage: 'field_name_1' => array('encrypt'=>'AES_ENCRYPT', 'decrypt'=>'AES_DECRYPT', 'key'=>'encryptKey')
- * 'field_name_2' => array('encrypt'=>'AES_ENCRYPT', 'decrypt'=>'AES_DECRYPT', 'key'=>CConfig::get('text.encryptKey'))
- */
- protected function _encryptedFields()
- {
- return array();
- }
-
- /**
- * This method is invoked before saving a record (after validation, if any)
- * You may override this method
- * @param int $pk
- * @return boolean
- */
- protected function _beforeSave($pk = 0)
- {
- // $pk - key used for saving operation
- return true;
- }
-
- /**
- * This method is invoked after saving a record successfully
- * @param int $pk
- * You may override this method
- */
- protected function _afterSave($pk = 0)
- {
- // $pk - key used for saving operation
- // $this->_columns - array of columns, e.g. $this->_columns['is_active']
- // code here
- }
-
- /**
- * This method is invoked before deleting a record (after validation, if any)
- * You may override this method
- * @param int $pk
- * @return boolean
- */
- protected function _beforeDelete($pk = 0)
- {
- // $pk - key used for deleting operation
- return true;
- }
-
- /**
- * This method is invoked after deleting a record successfully
- * @param int $pk
- * You may override this method
- */
- protected function _afterDelete($pk = 0)
- {
- // $pk - key used for deleting operation
- // code here
- }
-
- /**
- * Prepares custom fields for query
- * @return string
- */
- private function _getCustomFields()
- {
- $result = '';
- $fields = $this->_customFields();
- if (is_array($fields)) {
- foreach ($fields as $key => $val) {
- $result .= ', ' . $key . ' as ' . $val;
- }
- }
-
- return $result;
- }
-
- /**
- * Add custom fields for query
- */
- private function _addCustomFields()
- {
- $fields = $this->_customFields();
- if (is_array($fields)) {
- foreach ($fields as $key => $val) {
- $this->_columns[$val] = '';
- $this->_columnTypes[$val] = 'varchar';
- }
- }
- }
-
- /**
- * Remove custom fields for query
- */
- private function _removeCustomFields()
- {
- $fields = $this->_customFields();
- if (is_array($fields)) {
- foreach ($fields as $key => $val) {
- unset($this->_columns[$val]);
- unset($this->_columnTypes[$val]);
- }
- }
- }
-
- /**
- * Prepares relations for query
- * @return string
- */
- private function _getRelations()
- {
- $result = array('fields' => '', 'tables' => '', 'fieldsArray' => array());
- $rel = $this->_relations();
- if (!is_array($rel)) return $result;
- $defaultJoinType = self::LEFT_OUTER_JOIN;
- $nl = "\n";
-
- foreach ($rel as $key => $val) {
- $key = isset($val['parent_key']) ? $val['parent_key'] : $key;
- $relationType = isset($val[0]) ? $val[0] : '';
- $relatedTable = isset($val[1]) ? $val[1] : '';
- $relatedTableKey = isset($val[2]) ? $val[2] : '';
- $joinType = (isset($val['joinType']) && in_array($val['joinType'], self::$_joinTypes)) ? $val['joinType'] : $defaultJoinType;
- $condition = isset($val['condition']) ? $val['condition'] : '';
-
- if (
- $relationType == self::HAS_ONE ||
- $relationType == self::BELONGS_TO ||
- $relationType == self::HAS_MANY ||
- $relationType == self::MANY_MANY
- ) {
- if (isset($val['fields']) && is_array($val['fields'])) {
- foreach ($val['fields'] as $field => $fieldAlias) {
- if (is_numeric($field)) {
- $field = $fieldAlias;
- $fieldAlias = '';
- }
- $result['fields'] .= ', ' . $this->_tableName($relatedTable) . '.' . $field . (!empty($fieldAlias) ? ' as ' . $fieldAlias : '');
- $result['fieldsArray'][] = (!empty($fieldAlias) ? $fieldAlias : $field);
- }
- } else {
- $result['fields'] .= ', ' . $this->_tableName($relatedTable) . '.*';
- }
- $result['tables'] .= $joinType . ' ' . $this->_tableName($relatedTable) . ' ON ' . $this->_tableName() . '.' . $key . ' = ' . $this->_tableName($relatedTable) . '.' . $relatedTableKey;
- $result['tables'] .= (($condition != '') ? ' AND ' . $condition : '') . $nl;
- }
- }
-
- return $result;
- }
-
- /**
- * Prepare LIMIT clause for SQL statement
- * @param string $limit
- * @retun array
- */
- private function _prepareLimit($limit = '')
- {
- $limits = array('before' => '', 'after' => '');
-
- if (!empty($limit)) {
- if (preg_match('/mssql|sqlsrv/i', $this->_dbDriver)) {
- $limits['before'] = !empty($limit) ? ' TOP ' . $limit : '';
- } else {
- $limits['after'] = !empty($limit) ? ' LIMIT ' . $limit : '';
- }
- }
-
- return $limits;
- }
-
- /**
- * Escapes table name with backquotes and adds db prefix
- * Prepares table name for using in SQL statements
- * @param string $table
- * @return string
- */
- private function _tableName($table = '')
- {
- if (empty($table)) {
- $table = $this->_table;
- }
-
- return $this->_backQuote . $this->_dbPrefix . $table . $this->_backQuote;
- }
-
- /**
- * Checks if a given field is encrypted field
- * @param string $column
- * @return bool
- */
- private function _isEncryptedField($column = '')
- {
- $encryptedFields = $this->_encryptedFields();
- return isset($encryptedFields[$column]) ? true : false;
- }
-
- /**
- * Prepares encrypted fields for query
- * @return string
- */
- private function _getEncryptedFields()
- {
- $result = '';
- $fields = $this->_encryptedFields();
- if (is_array($fields)) {
- foreach ($fields as $key => $val) {
- $encryptedField = $this->_getEncryptedField($key);
- $result .= ', ' . $encryptedField['decrypt'] . '(' . $key . ',"' . $encryptedField['key'] . '") as ' . $key;
- }
- }
-
- return $result;
- }
-
- /**
- * Returns encrypted field info
- * @param string $column
- * @return array
- */
- private function _getEncryptedField($column = '')
- {
- $encryptedFields = $this->_encryptedFields();
- return isset($encryptedFields[$column]) ? $encryptedFields[$column] : array();
- }
+ '.$attributes_clause.'
+ '.$whereClause.'
+ '.$orderBy.'
+ '.$limits['after'];
+
+ return $this->_db->select($sql, $params);
+ }
+
+ /**
+ * This method queries your database to find all related objects
+ * Ex.: findAll('post_id = :postID AND is_active = :isActive', array(':postID'=>10, ':isActive'=>1));
+ * Ex.: findAll(array('condition'=>'post_id = :postID AND is_active = :isActive', 'select'=>'', 'group|groupBy'=>'', 'order|orderBy'=>'id DESC', 'limit'=>'0, 10'), array(':postID'=>10, ':isActive'=>1));
+ * Ex.: findAll(CConfig::get('db.prefix').$this->_tableTranslation.'.news_text LIKE :keywords', array(':keywords'=>'%'.$keywords.'%'));
+ *
+ * @param mixed $conditions
+ * @param array $params 'select': MAX(date), name or CConfig::get('db.prefix').table.field_name etc. - actually for ONLY_FULL_GROUP_BY mode
+ * 'groupBy': table.field_name or field_name
+ * @param bool|string $cacheId
+ * @param int $fetchMode
+ *
+ * @return array
+ */
+ public function findAll($conditions = '', $params = [], $cacheId = false, $fetchMode = PDO::FETCH_ASSOC)
+ {
+ if (is_array($conditions)) {
+ $where = isset($conditions['condition']) ? $conditions['condition'] : '';
+ if (isset($conditions['group'])) {
+ $group = isset($conditions['group']) ? $conditions['group'] : '';
+ } elseif (isset($conditions['groupBy'])) {
+ $group = isset($conditions['groupBy']) ? $conditions['groupBy'] : '';
+ }
+ if (isset($conditions['order'])) {
+ $order = isset($conditions['order']) ? $conditions['order'] : '';
+ } elseif (isset($conditions['orderBy'])) {
+ $order = isset($conditions['orderBy']) ? $conditions['orderBy'] : '';
+ }
+ $limit = isset($conditions['limit']) ? $conditions['limit'] : '';
+ $select = isset($conditions['select']) ? $conditions['select'] : '';
+ } else {
+ $where = $conditions;
+ $group = '';
+ $order = '';
+ $limit = '';
+ $select = '';
+ }
+
+ $whereClause = ! empty($where) ? ' WHERE '.$where : '';
+ $groupBy = ! empty($group) ? ' GROUP BY '.$group : '';
+ $orderBy = ! empty($order) ? ' ORDER BY '.$order : '';
+ $limits = $this->_prepareLimit($limit);
+ $selectList = ! empty($select) ? $select : $this->_tableName().'.*';
+
+ $relations = $this->_getRelations();
+ $customFields = $this->_getCustomFields();
+ $encryptedField = $this->_getEncryptedFields();
+
+ $sql = 'SELECT
+ '.$limits['before'].'
+ '.$selectList.'
+ '.$relations['fields'].'
+ '.$customFields.'
+ '.$encryptedField.'
+ FROM '.$this->_tableName().'
+ '.$relations['tables'].'
+ '.$whereClause.'
+ '.$groupBy.'
+ '.$orderBy.'
+ '.$limits['after'];
+
+ return $this->_db->select($sql, $params, 'fetchAll', $fetchMode, $cacheId);
+ }
+
+ /**
+ * This method queries your database to find first related record primary key
+ * Ex.: findPk('postID = :postID AND isActive = :isActive', array(':postID'=>10, ':isActive'=>1));
+ *
+ * @param mixed $conditions
+ * @param array $params
+ *
+ * @return int
+ */
+ public function findPk($conditions = '', $params = [])
+ {
+ if ($result = $this->find($conditions, $params)) {
+ $key = $this->_primaryKey;
+
+ return $result->$key;
+ }
+
+ return '';
+ }
+
+ /**
+ * Create new record
+ *
+ * @param array $data
+ * @param bool $preOperations
+ *
+ * @return bool
+ */
+ public function create($data = [], $preOperations = true)
+ {
+ $allowOperation = true;
+ if ($preOperations) {
+ if ( ! $this->_beforeSave($this->_pkValue)) {
+ $allowOperation = false;
+ CDebug::AddMessage(
+ 'errors',
+ 'before-save',
+ A::t('core', 'AR before operation on table: {table}', ['{table}' => $this->_table])
+ );
+ }
+ }
+
+ if ($allowOperation) {
+ $result = $this->_db->insert($this->_table, $data);
+ $this->_isNewRecord = true;
+ // Save last inset ID
+ $this->_pkValue = (int)$result;
+
+ if ($result) {
+ if ($preOperations) {
+ $this->_afterSave($this->_pkValue);
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Update existing record
+ *
+ * @param int $id
+ * @param array $data
+ * @param bool $preOperations
+ * @param bool $forceSave
+ *
+ * @return boolean
+ */
+ public function update($id, $data = [], $preOperations = true, $forceSave = false)
+ {
+ $allowOperation = true;
+ if ($preOperations) {
+ if ( ! $this->_beforeSave($id)) {
+ $allowOperation = false;
+ CDebug::AddMessage(
+ 'errors',
+ 'before-save',
+ A::t('core', 'AR before operation on table: {table}', ['{table}' => $this->_table])
+ );
+ }
+ }
+
+ if ($allowOperation) {
+ $result = $this->_db->update(
+ $this->_table,
+ $data,
+ $this->_primaryKey.' = :primary_key',
+ [':primary_key' => (int)$id],
+ $forceSave
+ );
+ $this->_isNewRecord = false;
+
+ if ($result) {
+ if ($preOperations) {
+ $this->_afterSave($id);
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Save data
+ *
+ * @param CRecordEntity $entity
+ * @param bool $forceSave
+ *
+ * @return boolean
+ */
+ public function save($entity = null, $forceSave = false)
+ {
+ $data = [];
+ $this->_removeCustomFields();
+
+ if ( ! is_null($entity) && ($entity instanceof CRecordEntity)) {
+ // ---------------------------------------
+ // Handle Entity
+ // ---------------------------------------
+ $columns = $entity->allowedColumns();
+ $primaryKey = $entity->primaryKey();
+ $pkValue = $entity->getPrimaryKey();
+
+ foreach ($columns as $column => $val) {
+ if ($column != 'id' && $column != $primaryKey) {
+ $data[$column] = $entity->$column;
+ }
+ }
+
+ if ($pkValue > 0) {
+ $result = $this->_db->update(
+ $this->_table,
+ $data,
+ $primaryKey.' = :primary_key',
+ [':primary_key' => (int)$pkValue],
+ $forceSave
+ );
+ } else {
+ $result = $this->_db->insert($this->_table, $data, $forceSave);
+ // Save last inset ID
+ $pkValue = (int)$result;
+ }
+
+ if ($result) {
+ $this->_afterSave($pkValue);
+
+ return true;
+ }
+ } else {
+ // ---------------------------------------
+ // Handle Model
+ // ---------------------------------------
+ if ($this->_beforeSave($this->_pkValue)) {
+ $columns = $this->allowedColumns();
+ foreach ($columns as $column => $val) {
+ $relations = $this->_getRelations();
+ if ($column != 'id' && $column != $this->_primaryKey
+ && ! in_array(
+ $column,
+ $relations['fieldsArray']
+ )
+ ) { // && ($column != 'created_at') && !$NEW)
+ //$value = $this->$column;
+ //if(array_search($this->_columnTypes[$column], array('int', 'float', 'decimal'))){
+ // $value = filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
+ //}
+ if ($this->_isEncryptedField($column)) {
+ $encryptedField = $this->_getEncryptedField($column);
+ $data[$column] = [
+ 'param_key' => $encryptedField['encrypt'].'('.$column.',"'.$encryptedField['key']
+ .'")',
+ 'param_value' => $this->$column
+ ];
+ } else {
+ $data[$column] = $this->$column;
+ }
+ }
+ }
+
+ if ($this->_pkValue > 0) {
+ $result = $this->_db->update(
+ $this->_table,
+ $data,
+ $this->_primaryKey.' = :primary_key',
+ [':primary_key' => (int)$this->_pkValue],
+ $forceSave
+ );
+ $this->_isNewRecord = false;
+ } else {
+ $result = $this->_db->insert($this->_table, $data, $forceSave);
+ $this->_isNewRecord = true;
+ // Save last inset ID
+ $this->_pkValue = (int)$result;
+ }
+
+ if ($result) {
+ $this->_afterSave($this->_pkValue);
+
+ return true;
+ }
+ } else {
+ CDebug::AddMessage(
+ 'errors',
+ 'before-save',
+ A::t('core', 'AR before operation on table: {table}', ['{table}' => $this->_table])
+ );
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Clear primary key
+ *
+ * @return boolean
+ */
+ public function clearPkValue()
+ {
+ $this->_pkValue = 0;
+
+ return true;
+ }
+
+ /**
+ * Reset the object with fields
+ *
+ * @return boolean
+ */
+ public function reset()
+ {
+ $this->_columns = [];
+ $this->_specialFields = [];
+
+ if ( ! empty($this->_table)) {
+ $this->_createObjectFromTable();
+ $this->_pkValue = 0;
+ }
+
+ return true;
+ }
+
+ /**
+ * Updates records with the specified primary key
+ * See {@link find()} for detailed explanation about $conditions
+ * Ex.: updateByPk($pk, array('name'=>$value), 'postID = 10 AND isActive = 1');
+ *
+ * @param string $pk
+ * @param array $data
+ * @param mixed $conditions
+ * @param array $params
+ *
+ * @return bool
+ */
+ public function updateByPk($pk, $data = [], $conditions = '', $params = [])
+ {
+ if ($this->_beforeSave($pk)) {
+ if (is_array($conditions)) {
+ $where = isset($conditions['condition']) ? $conditions['condition'] : '';
+ } else {
+ $where = $conditions;
+ }
+ $whereClause = ! empty($where) ? ' AND '.$where : '';
+ $params[':primary_key'] = (int)$pk;
+
+ $result = $this->_db->update(
+ $this->_table,
+ $data,
+ $this->_primaryKey.' = :primary_key'.$whereClause,
+ $params
+ );
+ if ($result) {
+ $this->_afterSave($pk);
+
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ CDebug::AddMessage(
+ 'errors',
+ 'before-update',
+ A::t('core', 'AR before operation on table: {table}', ['{table}' => $this->_table])
+ );
+ }
+ }
+
+ /**
+ * Updates the rows matching the specified condition
+ * Ex.: updateAll(array('name'=>$value), 'postID = 10 AND isActive = 1');
+ * Ex.: updateAll(array('name'=>$value), 'postID = 10 AND isActive = :isActive', array(':isActiv'=>1));
+ *
+ * @param array $data
+ * @param mixed $conditions
+ * @param array $params
+ *
+ * @return bool
+ */
+ public function updateAll($data = [], $conditions = '', $params = [])
+ {
+ if (is_array($conditions)) {
+ $where = isset($conditions['condition']) ? $conditions['condition'] : '';
+ } else {
+ $where = $conditions;
+ }
+ $whereClause = ! empty($where) ? $where : '1';
+
+ $result = $this->_db->update($this->_table, $data, $whereClause, $params);
+ if ($result) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Remove the row from database if AR instance has been populated with this row
+ * Ex.: $post = PostModel::model()->findByPk(10);
+ * $post->delete();
+ *
+ * @return boolean
+ */
+ public function delete()
+ {
+ if ( ! empty($this->_pkValue) && $this->deleteByPk($this->_pkValue)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Remove the rows matching the specified condition and primary key(s)
+ * Ex.: deleteByPk(10, 'postID = :postID AND isActive = :isActive', array(':postID'=>10, ':isActive'=>1));
+ *
+ * @param string $pk
+ * @param mixed $conditions
+ * @param array $params
+ *
+ * @return boolean
+ */
+ public function deleteByPk($pk, $conditions = '', $params = [])
+ {
+ if ($this->_beforeDelete($pk)) {
+ if (is_array($conditions)) {
+ $where = isset($conditions['condition']) ? $conditions['condition'] : '';
+ } else {
+ $where = $conditions;
+ }
+ $whereClause = ! empty($where) ? ' WHERE '.$where : '';
+ $params[':primary_key'] = (int)$pk;
+
+ $result = $this->_db->delete($this->_table, $this->_primaryKey.' = :primary_key'.$whereClause, $params);
+ if ($result) {
+ $this->_afterDelete($pk);
+
+ return true;
+ }
+ } else {
+ CDebug::AddMessage(
+ 'errors',
+ 'before-delete',
+ A::t(
+ 'core',
+ 'AR before delete operation on table: {table}',
+ ['{table}' => $this->_table]
+ )
+ );
+ }
+
+ return false;
+ }
+
+ /**
+ * Remove the rows matching the specified condition
+ * Ex.: deleteAll('postID = :postID AND isActive = :isActive', array(':postID'=>10, ':isActive'=>1));
+ * Ex.: deleteAll(array('condition'=>'postID = :postID AND isActive = :isActive'), array(':postID'=>10, ':isActive'=>1));
+ *
+ * @param mixed $conditions
+ * @param array $params
+ *
+ * @return boolean
+ */
+ public function deleteAll($conditions = '', $params = [])
+ {
+ if ($this->_beforeDelete()) {
+ if (is_array($conditions)) {
+ $where = isset($conditions['condition']) ? $conditions['condition'] : '';
+ } else {
+ $where = $conditions;
+ }
+ $whereClause = ! empty($where) ? ' WHERE '.$where : '';
+
+ $result = $this->_db->delete($this->_table, $whereClause, $params);
+ if ($result) {
+ $this->_afterDelete();
+
+ return true;
+ }
+ } else {
+ CDebug::AddMessage(
+ 'errors',
+ 'before-delete',
+ A::t(
+ 'core',
+ 'AR before delete operation on table: {table}',
+ ['{table}' => $this->_table]
+ )
+ );
+ }
+
+ return false;
+ }
+
+ /**
+ * This method selects distinct value
+ *
+ * @param string $field
+ *
+ * @return array
+ */
+ public function distinct($field = '')
+ {
+ return $this->findAll(['group' => $this->_tableName().'.'.$field]);
+ }
+
+ /**
+ * This method reloads model data according to the current primary key
+ *
+ * @return object
+ */
+ public function refresh()
+ {
+ return $this->findByPk($this->_pkValue);
+ }
+
+ /**
+ * This method check if there is at least one row satisfying the specified condition
+ * Ex.: exists('postID = :postID AND isActive = :isActive', array(':postID'=>10, ':isActive'=>1));
+ *
+ * @param mixed $conditions
+ * @param array $params
+ *
+ * @return bolean
+ */
+ public function exists($conditions = '', $params = [])
+ {
+ if (is_array($conditions)) {
+ $where = isset($conditions['condition']) ? $conditions['condition'] : '';
+ } else {
+ $where = $conditions;
+ }
+
+ $whereClause = ! empty($where) ? ' WHERE '.$where : '';
+ $limits = $this->_prepareLimit('1');
+
+ $sql = 'SELECT
+ '.$limits['before'].'
+ '.$this->_tableName().'.*
+ FROM '.$this->_tableName().'
+ '.$whereClause.'
+ '.$limits['after'];
+
+ $result = $this->_db->select($sql, $params);
+
+ return ($result) ? true : false;
+ }
+
+ /**
+ * Finds the number of rows satisfying the specified query condition
+ * Ex.: count('postID = :postID AND isActive = :isActive', array(':postID'=>10, ':isActive'=>1));
+ * Ex.: count(array('condition'=>'post_id = :postID AND is_active = :isActive', 'select'=>'', 'count'=>'*', 'group|groupBy'=>'', 'order|orderBy'=>'', 'allRows'=>false), array(':postID'=>10, ':isActive'=>1));
+ *
+ * @param mixed $conditions
+ * @param array $params
+ *
+ * @return integer
+ */
+ public function count($conditions = '', $params = [])
+ {
+ if (is_array($conditions)) {
+ $where = isset($conditions['condition']) ? $conditions['condition'] : '';
+ if (isset($conditions['group'])) {
+ $group = isset($conditions['group']) ? $conditions['group'] : '';
+ } elseif (isset($conditions['groupBy'])) {
+ $group = isset($conditions['groupBy']) ? $conditions['groupBy'] : '';
+ }
+ if ( ! empty($group)) {
+ if (isset($conditions['order'])) {
+ $order = isset($conditions['order']) ? $conditions['order'] : '';
+ } elseif (isset($conditions['orderBy'])) {
+ $order = isset($conditions['orderBy']) ? $conditions['orderBy'] : '';
+ }
+ }
+ $count = isset($conditions['count']) ? $conditions['count'] : '*';
+ $select = isset($conditions['select']) ? $conditions['select'] : '';
+ $allRows = isset($conditions['allRows']) ? (bool)$conditions['allRows'] : false;
+ } else {
+ $where = $conditions;
+ $group = '';
+ $order = '';
+ $count = '*';
+ $select = '';
+ $allRows = false;
+ }
+
+ $whereClause = ! empty($where) ? ' WHERE '.$where : '';
+ $groupBy = ! empty($group) ? ' GROUP BY '.$group : '';
+ $orderBy = ! empty($order) ? ' ORDER BY '.$order : '';
+ $limits = $this->_prepareLimit(($allRows ? '' : '1'));
+ $relations = $this->_getRelations();
+
+ $sql = 'SELECT
+ '.$limits['before'].'
+ COUNT('.$count.') as cnt
+ '.($select ? ', '.$select : '').'
+ FROM '.$this->_tableName().'
+ '.$relations['tables'].'
+ '.$whereClause.'
+ '.$groupBy.'
+ '.$orderBy.'
+ '.$limits['after'];
+
+ $result = $this->_db->select($sql, $params);
+
+ if ($allRows) {
+ return (isset($result)) ? $result : null;
+ } else {
+ return (isset($result[0]['cnt'])) ? $result[0]['cnt'] : 0;
+ }
+ }
+
+ /**
+ * Finds a maximum value of the specified column
+ * Ex.: max('id', 'postID = :postID AND isActive = :isActive', array(':postID'=>10, ':isActive'=>1));
+ *
+ * @param string $column
+ * @param mixed $conditions
+ * @param array $params
+ *
+ * @return integer
+ */
+ public function max($column = '', $conditions = '', $params = [])
+ {
+ if (is_array($conditions)) {
+ $where = isset($conditions['condition']) ? $conditions['condition'] : '';
+ } else {
+ $where = $conditions;
+ }
+
+ $whereClause = ! empty($where) ? ' WHERE '.$where : '';
+ $column = ! empty($column) ? $this->_tableName().'.'.$column : $this->_primaryKey;
+ $relations = $this->_getRelations();
+ $limits = $this->_prepareLimit('1');
+
+ $sql = 'SELECT
+ '.$limits['before'].'
+ MAX('.$column.') as column_max
+ FROM '.$this->_tableName().'
+ '.$relations['tables'].'
+ '.$whereClause.'
+ '.$limits['after'];
+
+ $result = $this->_db->select($sql, $params);
+
+ return (isset($result[0]['column_max'])) ? $result[0]['column_max'] : 0;
+ }
+
+ /**
+ * Finds a minimum value of the specified column
+ * Ex.: min('id', 'postID = :postID AND isActive = :isActive', array(':postID'=>10, 'isActive'=>1));
+ *
+ * @param string $column
+ * @param mixed $conditions
+ * @param array $params
+ *
+ * @return integer
+ */
+ public function min($column = '', $conditions = '', $params = [])
+ {
+ if (is_array($conditions)) {
+ $where = isset($conditions['condition']) ? $conditions['condition'] : '';
+ } else {
+ $where = $conditions;
+ }
+
+ $whereClause = ! empty($where) ? ' WHERE '.$where : '';
+ $column = ! empty($column) ? $this->_tableName().'.'.$column : $this->_primaryKey;
+ $relations = $this->_getRelations();
+ $limits = $this->_prepareLimit('1');
+
+ $sql = 'SELECT
+ '.$limits['before'].'
+ MIN('.$column.') as column_min
+ FROM '.$this->_tableName().'
+ '.$relations['tables'].'
+ '.$whereClause.'
+ '.$limits['after'];
+
+ $result = $this->_db->select($sql, $params);
+
+ return (isset($result[0]['column_min'])) ? $result[0]['column_min'] : 0;
+ }
+
+ /**
+ * Finds a sum value of the specified column
+ * Ex.: sum('id', 'postID = :postID AND isActive = :isActive', array(':postID'=>10, ':isActive'=>1));
+ *
+ * @param string $column
+ * @param mixed $conditions
+ * @param array $params
+ *
+ * @return integer
+ */
+ public function sum($column = '', $conditions = '', $params = [])
+ {
+ if (is_array($conditions)) {
+ $where = isset($conditions['condition']) ? $conditions['condition'] : '';
+ } else {
+ $where = $conditions;
+ }
+
+ $whereClause = ! empty($where) ? ' WHERE '.$where : '';
+ $column = ! empty($column) ? $column : '';
+ $relations = $this->_getRelations();
+ $limits = $this->_prepareLimit('1');
+
+ $sql = 'SELECT
+ '.$limits['before'].'
+ SUM('.$column.') as column_sum
+ FROM '.$this->_tableName().'
+ '.$relations['tables'].'
+ '.$whereClause.'
+ '.$limits['after'];
+
+ $result = $this->_db->select($sql, $params);
+
+ return (isset($result[0]['column_sum'])) ? $result[0]['column_sum'] : 0;
+ }
+
+ /**
+ * Used to define relations between different tables in database and current $_table
+ * This method should be overridden
+ */
+ protected function _relations()
+ {
+ return [];
+ }
+
+ /**
+ * Used to define custom fields
+ * This method should be overridden
+ * Usage: 'CONCAT('.CConfig::get('db.prefix').$this->_table.'.last_name, " ", '.CConfig::get('db.prefix').$this->_table.'.first_name)' => 'fullname'
+ * '(SELECT COUNT(*) FROM '.CConfig::get('db.prefix').$this->_tableTranslation.')' => 'records_count'
+ */
+ protected function _customFields()
+ {
+ return [];
+ }
+
+ /**
+ * Used to define encrypted fields
+ * This method should be overridden
+ * Usage: 'field_name_1' => array('encrypt'=>'AES_ENCRYPT', 'decrypt'=>'AES_DECRYPT', 'key'=>'encryptKey')
+ * 'field_name_2' => array('encrypt'=>'AES_ENCRYPT', 'decrypt'=>'AES_DECRYPT', 'key'=>CConfig::get('text.encryptKey'))
+ */
+ protected function _encryptedFields()
+ {
+ return [];
+ }
+
+ /**
+ * This method is invoked before saving a record (after validation, if any)
+ * You may override this method
+ *
+ * @param int $pk
+ *
+ * @return boolean
+ */
+ protected function _beforeSave($pk = 0)
+ {
+ // $pk - key used for saving operation
+ return true;
+ }
+
+ /**
+ * This method is invoked after saving a record successfully
+ *
+ * @param int $pk
+ * You may override this method
+ */
+ protected function _afterSave($pk = 0)
+ {
+ // $pk - key used for saving operation
+ // $this->_columns - array of columns, e.g. $this->_columns['is_active']
+ // code here
+ }
+
+ /**
+ * This method is invoked before deleting a record (after validation, if any)
+ * You may override this method
+ *
+ * @param int $pk
+ *
+ * @return boolean
+ */
+ protected function _beforeDelete($pk = 0)
+ {
+ // $pk - key used for deleting operation
+ return true;
+ }
+
+ /**
+ * This method is invoked after deleting a record successfully
+ *
+ * @param int $pk
+ * You may override this method
+ */
+ protected function _afterDelete($pk = 0)
+ {
+ // $pk - key used for deleting operation
+ // code here
+ }
+
+ /**
+ * Prepares custom fields for query
+ *
+ * @return string
+ */
+ private function _getCustomFields()
+ {
+ $result = '';
+ $fields = $this->_customFields();
+ if (is_array($fields)) {
+ foreach ($fields as $key => $val) {
+ $result .= ', '.$key.' as '.$val;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Add custom fields for query
+ */
+ private function _addCustomFields()
+ {
+ $fields = $this->_customFields();
+ if (is_array($fields)) {
+ foreach ($fields as $key => $val) {
+ $this->_columns[$val] = '';
+ $this->_columnTypes[$val] = 'varchar';
+ }
+ }
+ }
+
+ /**
+ * Remove custom fields for query
+ */
+ private function _removeCustomFields()
+ {
+ $fields = $this->_customFields();
+ if (is_array($fields)) {
+ foreach ($fields as $key => $val) {
+ unset($this->_columns[$val]);
+ unset($this->_columnTypes[$val]);
+ }
+ }
+ }
+
+ /**
+ * Prepares relations for query
+ *
+ * @return string
+ */
+ private function _getRelations()
+ {
+ $result = ['fields' => '', 'tables' => '', 'fieldsArray' => []];
+ $rel = $this->_relations();
+ if ( ! is_array($rel)) {
+ return $result;
+ }
+ $defaultJoinType = self::LEFT_OUTER_JOIN;
+ $nl = "\n";
+
+ foreach ($rel as $key => $val) {
+ $key = isset($val['parent_key']) ? $val['parent_key'] : $key;
+ $relationType = isset($val[0]) ? $val[0] : '';
+ $relatedTable = isset($val[1]) ? $val[1] : '';
+ $relatedTableKey = isset($val[2]) ? $val[2] : '';
+ $joinType = (isset($val['joinType']) && in_array($val['joinType'], self::$_joinTypes))
+ ? $val['joinType'] : $defaultJoinType;
+ $condition = isset($val['condition']) ? $val['condition'] : '';
+
+ if (
+ $relationType == self::HAS_ONE || $relationType == self::BELONGS_TO || $relationType == self::HAS_MANY
+ || $relationType == self::MANY_MANY
+ ) {
+ if (isset($val['fields']) && is_array($val['fields'])) {
+ foreach ($val['fields'] as $field => $fieldAlias) {
+ if (is_numeric($field)) {
+ $field = $fieldAlias;
+ $fieldAlias = '';
+ }
+ $result['fields'] .= ', '.$this->_tableName($relatedTable).'.'.$field
+ .(! empty($fieldAlias) ? ' as '.$fieldAlias : '');
+ $result['fieldsArray'][] = (! empty($fieldAlias) ? $fieldAlias : $field);
+ }
+ } else {
+ $result['fields'] .= ', '.$this->_tableName($relatedTable).'.*';
+ }
+ $result['tables'] .= $joinType.' '.$this->_tableName($relatedTable).' ON '.$this->_tableName().'.'.$key
+ .' = '.$this->_tableName($relatedTable).'.'.$relatedTableKey;
+ $result['tables'] .= (($condition != '') ? ' AND '.$condition : '').$nl;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Prepare LIMIT clause for SQL statement
+ *
+ * @param string $limit
+ *
+ * @retun array
+ */
+ private function _prepareLimit($limit = '')
+ {
+ $limits = ['before' => '', 'after' => ''];
+
+ if ( ! empty($limit)) {
+ if (preg_match('/mssql|sqlsrv/i', $this->_dbDriver)) {
+ $limits['before'] = ! empty($limit) ? ' TOP '.$limit : '';
+ } else {
+ $limits['after'] = ! empty($limit) ? ' LIMIT '.$limit : '';
+ }
+ }
+
+ return $limits;
+ }
+
+ /**
+ * Escapes table name with backquotes and adds db prefix
+ * Prepares table name for using in SQL statements
+ *
+ * @param string $table
+ *
+ * @return string
+ */
+ private function _tableName($table = '')
+ {
+ if (empty($table)) {
+ $table = $this->_table;
+ }
+
+ return $this->_backQuote.$this->_dbPrefix.$table.$this->_backQuote;
+ }
+
+ /**
+ * Checks if a given field is encrypted field
+ *
+ * @param string $column
+ *
+ * @return bool
+ */
+ private function _isEncryptedField($column = '')
+ {
+ $encryptedFields = $this->_encryptedFields();
+
+ return isset($encryptedFields[$column]) ? true : false;
+ }
+
+ /**
+ * Prepares encrypted fields for query
+ *
+ * @return string
+ */
+ private function _getEncryptedFields()
+ {
+ $result = '';
+ $fields = $this->_encryptedFields();
+ if (is_array($fields)) {
+ foreach ($fields as $key => $val) {
+ $encryptedField = $this->_getEncryptedField($key);
+ $result .= ', '.$encryptedField['decrypt'].'('.$key.',"'.$encryptedField['key'].'") as '.$key;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns encrypted field info
+ *
+ * @param string $column
+ *
+ * @return array
+ */
+ private function _getEncryptedField($column = '')
+ {
+ $encryptedFields = $this->_encryptedFields();
+
+ return isset($encryptedFields[$column]) ? $encryptedFields[$column] : [];
+ }
}
diff --git a/framework/db/CDatabase.php b/framework/db/CDatabase.php
index 2f32096..dac23e9 100644
--- a/framework/db/CDatabase.php
+++ b/framework/db/CDatabase.php
@@ -39,796 +39,966 @@
class CDatabase extends PDO
{
-
- /** @var string */
- public static $count = 0;
-
- /** @var object */
- private static $_instance;
- /** @var string */
- private $_dbPrefix;
- /** @var string */
- private $_dbDriver;
- /** @var string */
- private $_dbName;
- /** @var bool */
- private $_cache;
- /** @var string */
- private $_cacheType;
- /** @var int */
- private $_cacheLifetime;
- /** @var string */
- private $_cacheDir;
- /** @var string */
- private $_query;
- /** @var char */
- private $_backQuote = '`';
- /** @var boolean */
- private static $_error;
- /** @var string */
- private static $_errorMessage;
-
- /**
- * Class default constructor
- * @param array $params
- */
- public function __construct($params = array())
- {
- // For direct use (e.g. setup module)
- if (!empty($params)) {
- $dbDriver = isset($params['dbDriver']) ? $params['dbDriver'] : '';
- $dbSocket = isset($params['dbSocket']) ? $params['dbSocket'] : '';
- $dbHost = isset($params['dbHost']) ? $params['dbHost'] : '';
- $dbPort = isset($params['dbPort']) ? $params['dbPort'] : '';
- $dbName = isset($params['dbName']) ? $params['dbName'] : '';
- $dbUser = isset($params['dbUser']) ? $params['dbUser'] : '';
- $dbPassword = isset($params['dbPassword']) ? $params['dbPassword'] : '';
- $dbCharset = isset($params['dbCharset']) ? $params['dbCharset'] : 'utf8';
-
- try {
- $this->_init($dbDriver, $dbSocket, $dbHost, $dbPort, $dbName, $dbUser, $dbPassword, $dbCharset);
- $this->_dbDriver = $dbDriver;
- $this->_dbName = $dbName;
- $this->_dbPrefix = '';
- } catch (Exception $e) {
- self::$_error = true;
- self::$_errorMessage = $e->getMessage();
- }
- } else {
- if (!A::app()->isSetup()) {
- if (APPHP_MODE == 'debug') {
- $startTime = CTime::getMicrotime();
- }
-
- try {
- if (CConfig::get('db') != '') {
- $this->_init(CConfig::get('db.driver'), CConfig::get('db.socket'), CConfig::get('db.host'), CConfig::get('db.port'), CConfig::get('db.database'), CConfig::get('db.username'), CConfig::get('db.password'), CConfig::get('db.charset', 'utf8'));
- } else {
- throw new Exception('Missing database configuration file');
- }
- } catch (Exception $e) {
- header('HTTP/1.1 503 Service Temporarily Unavailable');
- header('Status: 503 Service Temporarily Unavailable');
- $output = self::_fatalErrorPageContent();
- if (APPHP_MODE == 'debug') {
- $output = str_ireplace('{DESCRIPTION}', '' . A::t('core', 'This application is currently experiencing some database difficulties') . '
', $output);
- $output = str_ireplace(
- '{CODE}',
- 'Description: ' . $e->getMessage() . '
- File: ' . $e->getFile() . '
- Line: ' . $e->getLine(),
- $output
- );
- } else {
- $output = str_ireplace('{DESCRIPTION}', '' . A::t('core', 'This application is currently experiencing some database difficulties. Please check back again later') . '
', $output);
- $output = str_ireplace('{CODE}', A::t('core', 'For more information turn on debug mode in your application'), $output);
- }
- echo $output;
- exit(1);
- }
-
- $this->_dbDriver = CConfig::get('db.driver');
- $this->_dbName = CConfig::get('db.database');
- $this->_dbPrefix = CConfig::get('db.prefix');
-
- $this->_cache = CConfig::get('cache.db.enable') ? true : false;
- $this->_cacheType = in_array(CConfig::get('cache.db.type'), array('auto', 'manual')) ? CConfig::get('cache.db.type') : 'auto';
- $this->_cacheLifetime = CConfig::get('cache.db.lifetime', 0); /* in minutes */
- $this->_cacheDir = CConfig::get('cache.db.path'); /* protected/tmp/cache/ */
- if ($this->_cache) CDebug::addMessage('general', 'cache', 'enabled (' . $this->_cacheType . ') ');
-
- // Save data for debug
- if (APPHP_MODE == 'debug') {
- $finishTime = CTime::getMicrotime();
- $sqlConnectionTime = round((float)$finishTime - (float)$startTime, 5);
- CDebug::addSqlConnectionTime($sqlConnectionTime);
- }
- }
- }
-
- // Set back quote according to database driver
- if (!empty($this->_dbDriver) && preg_match('/mssql|sqlsrv/i', $this->_dbDriver)) {
- $this->_backQuote = '';
- }
- }
-
- /**
- * Initializes the database class
- * @param array $params
- */
- public static function init($params = array())
- {
- if (self::$_instance == null) self::$_instance = new self($params);
- return self::$_instance;
- }
-
- /**
- * Sets cache off
- */
- public function cacheOn()
- {
- $this->_setCaching(true);
- }
-
- /**
- * Sets cache off
- */
- public function cacheOff()
- {
- $this->_setCaching(false);
- }
-
- /**
- * Performs select query
- * @param string $sql SQL string
- * @param array $params parameters to bind
- * @param string $method (e.g 'fetch' or 'fetchAll')
- * @param constant $fetchMode PDO fetch mode
- * @param bool|string $cacheId cache identification
- * @return mixed - an array containing all of the result set rows
- * Ex.: Array([0] => Array([id] => 11, [name] => John), ...)
- */
- public function select($sql, $params = array(), $method = 'fetchAll', $fetchMode = PDO::FETCH_ASSOC, $cacheId = '')
- {
- if (APPHP_MODE == 'debug') {
- $startTime = CTime::getMicrotime();
- }
-
- $sth = $this->prepare($sql);
- $cacheContent = null;
- $error = false;
-
- try {
- if ($this->_isCacheAllowed($cacheId)) {
- $cacheFile = !empty($cacheId) ? $cacheId : $sql . (is_array($params) ? implode('|', $params) : '');
- $cacheContent = CCache::getContent(
- $this->_cacheDir . md5($cacheFile) . '.cch',
- $this->_cacheLifetime
- );
- }
-
- if (!$cacheContent) {
- if (is_array($params)) {
- foreach ($params as $key => $value) {
- if (is_array($value)) continue;
- list($key, $param) = $this->_prepareParams($key);
- $sth->bindValue($key, $value, $param);
- }
- }
- $sth->execute();
- $result = $sth->$method($fetchMode);
-
- if ($this->_isCacheAllowed($cacheId)) CCache::setContent($result, $this->_cacheDir);
- } else {
- $result = $cacheContent;
- }
- } catch (PDOException $e) {
- $this->_errorLog('select [database.php, ln.:' . $e->getLine() . ']', $e->getMessage() . ' => ' . $this->_interpolateQuery($sql, $params));
- $result = false;
- $error = true;
- }
-
- // Interpolate query and save it
- $this->_query = $this->_interpolateQuery($sql, $params);
-
- // Save data for debug
- if (APPHP_MODE == 'debug') {
- $finishTime = CTime::getMicrotime();
- $sqlTotalTime = round((float)$finishTime - (float)$startTime, 5);
- CDebug::addSqlTime($sqlTotalTime);
- CDebug::addMessage('queries', ++self::$count . '. select | ' . $sqlTotalTime . ' ' . A::t('core', 'sec') . '. | ' . A::t('core', 'total') . ': ' . (($result) ? count($result) : '0 (' . ($error ? 'error' : 'empty') . ' )') . ($cacheContent ? ' [cached] ' : '') . ' ', $this->_query);
- }
-
- return $result;
- }
-
- /**
- * Performs insert query
- * @param string $table name of the table to insert into
- * @param array $data associative array
- * @param bool $forceUpdate used to force update on Demo mode
- * @return int|boolean
- */
- public function insert($table, $data, $forceUpdate = false)
- {
- if(APPHP_MODE == 'demo' && !$forceUpdate){
- self::$_errorMessage = A::t('core', 'This operation is blocked in Demo Mode!');
- return false;
- }
-
- if (APPHP_MODE == 'debug') {
- $startTime = CTime::getMicrotime();
- }
-
- ksort($data);
-
- $fieldNames = $this->_quotes(implode($this->_backQuote . ', ' . $this->_backQuote, array_keys($data)));
-
- $fieldValues = '';
- if (is_array($data)) {
- foreach ($data as $key => $value) {
- // Regular fields: :last_name,
- // Encrypted fields: AES_ENCRYPT(:last_name, "key"),
- // Use str_replace('('.$key.',', '(:'.$key.',', ... for encrypted fields
- $fieldValues .= (is_array($value) ? str_replace('(' . $key . ',', '(:' . $key . ',', $value['param_key']) : ':' . $key) . ',';
- }
- $fieldValues = rtrim($fieldValues, ',');
- }
-
- $sql = 'INSERT INTO ' . $this->_quotes($this->_dbPrefix . $table) . ' (' . $fieldNames . ') VALUES (' . $fieldValues . ')';
- $sth = $this->prepare($sql);
-
- if (is_array($data)) {
- foreach ($data as $key => $value) {
- list($key, $param) = $this->_prepareParams($key);
- $sth->bindValue(':' . $key, (is_array($value) ? $value['param_value'] : $value), $param);
- }
- }
-
- try {
- $sth->execute();
- $result = $this->lastInsertId();
- } catch (PDOException $e) {
- $this->_errorLog('insert [database.php, ln.:' . $e->getLine() . ']', $e->getMessage() . ' => ' . $this->_interpolateQuery($sql, $data));
- $result = false;
- }
-
- // Interpolate query and save it
- $this->_query = $this->_interpolateQuery($sql, $data);
-
- // Save data for debug
- if (APPHP_MODE == 'debug') {
- $finishTime = CTime::getMicrotime();
- $sqlTotalTime = round((float)$finishTime - (float)$startTime, 5);
- CDebug::addSqlTime($sqlTotalTime);
- CDebug::addMessage('queries', ++self::$count . '. insert | ' . $sqlTotalTime . ' ' . A::t('core', 'sec') . '. | ID: ' . (($result) ? $result : '0 (error )') . ' ', $this->_query);
- }
-
- return $result;
- }
-
- /**
- * Performs update query
- * @param string $table name of table to update
- * @param array $data an associative array
- * @param string $where the WHERE clause of query
- * @param array $params , ex.: array('is_default'=>0, 'rate'=>array('expression'=>'ROUND(rate/1.2,4)'))
- * @param bool $forceUpdate used to force update on Demo mode
- * @param boolean
- */
- public function update($table, $data, $where = '1', $params = array(), $forceUpdate = false)
- {
- if (APPHP_MODE == 'demo' && !$forceUpdate) {
- self::$_errorMessage = A::t('core', 'This operation is blocked in Demo Mode!');
- return false;
- }
-
- if (APPHP_MODE == 'debug') {
- $startTime = CTime::getMicrotime();
- }
-
- ksort($data);
-
- $fieldDetails = NULL;
- if (is_array($data)) {
- foreach ($data as $key => $value) {
- // Regular fields: `last_name` = :last_name, : 'last_name'=>'John',
- // Expression: 'rate'=>'ROUND(rate/'.$this->rate.')' : 'rate'=>array('expression'=>'ROUND(rate/'.$this->rate.',4)',
- // Encrypted fields: 'last_name'=>'AES_ENCRYPT(:last_name, "key")' : `last_name` = AES_ENCRYPT(:last_name, "key"),
- // Use str_replace('('.$key.',', '(:'.$key.',', ... for encrypted fields
- if (isset($value['expression'])) {
- $fieldDetails .= $this->_quotes($key) . ' = ' . $value['expression'] . ',';
- } else {
- $fieldDetails .= $this->_quotes($key) . ' = ' . (is_array($value) ? str_replace('(' . $key . ',', '(:' . $key . ',', $value['param_key']) : ':' . $key) . ',';
- }
- }
- }
- $fieldDetails = rtrim($fieldDetails, ',');
- $sql = 'UPDATE ' . $this->_quotes($this->_dbPrefix . $table) . ' SET ' . $fieldDetails . ' WHERE ' . $where;
-
- $sth = $this->prepare($sql);
- if (is_array($data)) {
- foreach ($data as $key => $value) {
- if (!isset($value['expression'])) {
- list($key, $param) = $this->_prepareParams($key);
- $sth->bindValue(':' . $key, (is_array($value) ? $value['param_value'] : $value), $param);
- }
- }
- }
- if (is_array($params)) {
- foreach ($params as $key => $value) {
- list($key, $param) = $this->_prepareParams($key);
- $sth->bindValue($key, $value, $param);
- }
- }
-
- try {
- $sth->execute();
- // $result = $sth->rowCount();
- $result = true;
- } catch (PDOException $e) {
- // Get trace from parent level
- // $trace = $e->getTrace();
- // echo '';
- // echo $trace[1]['file'];
- // echo $trace[1]['line'];
- // echo ' ';
- $this->_errorLog('update [database.php, ln.:' . $e->getLine() . ']', $e->getMessage() . ' => ' . $this->_interpolateQuery($sql, $data));
- $result = false;
- }
-
- // Interpolate query and save it
- $this->_query = $this->_interpolateQuery($sql, $data);
-
- // Save data for debug
- if (APPHP_MODE == 'debug') {
- $finishTime = CTime::getMicrotime();
- $sqlTotalTime = round((float)$finishTime - (float)$startTime, 5);
- CDebug::addSqlTime($sqlTotalTime);
- CDebug::addMessage('queries', ++self::$count . '. update | ' . $sqlTotalTime . ' ' . A::t('core', 'sec') . '. | ' . A::t('core', 'total') . ': ' . (($result) ? $sth->rowCount() : '0 (error )') . ' ', $this->_query);
- }
-
- return $result;
- }
-
- /**
- * Performs delete query
- * @param string $table
- * @param string $where the WHERE clause of query
- * @param array $params
- * @return bool|int affected rows or false
- */
- public function delete($table, $where = '', $params = array())
- {
- if (APPHP_MODE == 'demo') {
- self::$_errorMessage = A::t('core', 'This operation is blocked in Demo Mode!');
- return false;
- }
-
- if (APPHP_MODE == 'debug') {
- $startTime = CTime::getMicrotime();
- }
-
- $where_clause = (!empty($where) && !preg_match('/\bwhere\b/i', $where)) ? ' WHERE ' . $where : $where;
- $sql = 'DELETE FROM ' . $this->_quotes($this->_dbPrefix . $table) . ' ' . $where_clause;
-
- $sth = $this->prepare($sql);
- if (is_array($params)) {
- foreach ($params as $key => $value) {
- list($key, $param) = $this->_prepareParams($key);
- $sth->bindValue($key, $value, $param);
- }
- }
-
- try {
- $sth->execute();
- $result = $sth->rowCount();
- } catch (PDOException $e) {
- $this->_errorLog('delete [database.php, ln.:' . $e->getLine() . ']', $e->getMessage() . ' => ' . $this->_interpolateQuery($sql, $params));
- $result = false;
- }
-
- // Interpolate query and save it
- $this->_query = $this->_interpolateQuery($sql, $params);
-
- // Save data for debug
- if (APPHP_MODE == 'debug') {
- $finishTime = CTime::getMicrotime();
- $sqlTotalTime = round((float)$finishTime - (float)$startTime, 5);
- CDebug::addSqlTime($sqlTotalTime);
- CDebug::addMessage('queries', ++self::$count . '. delete | ' . $sqlTotalTime . ' ' . A::t('core', 'sec') . '. | ' . A::t('core', 'total') . ': ' . (($result) ? $result : '0 (warning )') . ' ', $this->_query);
- }
-
- return $result;
- }
-
- /**
- * Builds and executes a SQL statement for truncating a DB table
- * @param string $table the table to be truncated
- * @return bool|int affected rows or false
- * @since 1.1.0
- */
- public function truncate($table)
- {
- if (APPHP_MODE == 'demo') {
- self::$_errorMessage = A::t('core', 'This operation is blocked in Demo Mode!');
- return false;
- }
-
- if (APPHP_MODE == 'debug') {
- $startTime = CTime::getMicrotime();
- }
-
- $sql = ' TRUNCATE TABLE ' . $this->_quotes($this->_dbPrefix . $table);
- $sth = $this->prepare($sql);
-
- try {
- $sth->execute();
- $result = $sth->rowCount();
- } catch (PDOException $e) {
- $this->_errorLog('delete [database.php, ln.:' . $e->getLine() . ']', $e->getMessage() . ' => ' . $sql);
- $result = false;
- }
-
- // Save query
- $this->_query = $sql;
-
- // Save data for debug
- if (APPHP_MODE == 'debug') {
- $finishTime = CTime::getMicrotime();
- $sqlTotalTime = round((float)$finishTime - (float)$startTime, 5);
- CDebug::addSqlTime($sqlTotalTime);
- CDebug::addMessage('queries', ++self::$count . '. truncate | ' . $sqlTotalTime . ' ' . A::t('core', 'sec') . '. | ' . A::t('core', 'total') . ': ' . (($result) ? $result : '0 (warning )') . ' ', $this->_query);
- }
-
- return $result;
- }
-
- /**
- * Returns ID of the last inserted record
- * @return int
- */
- public function lastId()
- {
- return (!empty($this)) ? $this->lastInsertId() : 0;
- }
-
- /**
- * Returns last query
- * @return string
- */
- public function lastQuery()
- {
- return $this->_query;
- }
-
- /**
- * Performs a standard query
- * @param string $sql
- * @param array $params
- * @param constant $fetchMode PDO fetch mode
- * @return mixed - an array containing all of the result set rows
- */
- public function customQuery($sql, $params = array(), $fetchMode = PDO::FETCH_ASSOC)
- {
- if (APPHP_MODE == 'demo') {
- self::$_errorMessage = A::t('core', 'This operation is blocked in Demo Mode!');
- return false;
- }
-
- if (APPHP_MODE == 'debug') {
- $startTime = CTime::getMicrotime();
- }
-
- try {
- if (is_array($params) && !empty($params)) {
- $sth = $this->prepare($sql);
- foreach ($params as $key => $value) {
- list($key, $param) = $this->_prepareParams($key);
- $sth->bindValue($key, $value, $param);
- }
- $sth->execute();
- } else {
- $sth = $this->query($sql);
- }
- $result = $sth->fetchAll($fetchMode);
- } catch (PDOException $e) {
- $this->_errorLog('customQuery [database.php, ln.:' . $e->getLine() . ']', $e->getMessage() . ' => ' . $sql);
- $result = false;
- }
-
- // Interpolate query and save it
- $this->_query = $this->_interpolateQuery($sql, $params);
-
- // Save data for debug
- if (APPHP_MODE == 'debug') {
- $finishTime = CTime::getMicrotime();
- $sqlTotalTime = round((float)$finishTime - (float)$startTime, 5);
- CDebug::addSqlTime($sqlTotalTime);
- CDebug::addMessage('queries', ++self::$count . '. query | ' . $sqlTotalTime . ' ' . A::t('core', 'sec') . '. | ' . A::t('core', 'total') . ': ' . (($result) ? count($result) : '0 (error )') . ' ', $this->_query);
- }
-
- return $result;
- }
-
- /**
- * Performs a standard exec
- * @param string $sql
- * @param array $params
- * @param bool $forceUpdate used to force update on Demo mode
- * @return boolean
- */
- public function customExec($sql, $params = array(), $forceUpdate = false)
- {
- if(APPHP_MODE == 'demo' && !$forceUpdate){
- self::$_errorMessage = A::t('core', 'This operation is blocked in Demo Mode!');
- return false;
- }
-
- if (APPHP_MODE == 'debug') {
- $startTime = CTime::getMicrotime();
- }
-
- try {
- if (is_array($params) && !empty($params)) {
- $sth = $this->prepare($sql);
- foreach ($params as $key => $value) {
- list($key, $param) = $this->_prepareParams($key);
- $sth->bindValue($key, $value, $param);
- }
- $sth->execute();
- $result = $sth->rowCount();
- } else {
- $result = $this->exec($sql);
- }
- } catch (PDOException $e) {
- $this->_errorLog('customExec [database.php, ln.:' . $e->getLine() . ']', $e->getMessage() . ' => ' . $sql);
- $result = false;
- }
-
- // Interpolate query and save it
- $this->_query = $this->_interpolateQuery($sql, $params);
-
- // Save data for debug
- if (APPHP_MODE == 'debug') {
- $finishTime = CTime::getMicrotime();
- $sqlTotalTime = round((float)$finishTime - (float)$startTime, 5);
- CDebug::addSqlTime($sqlTotalTime);
- CDebug::addMessage('queries', ++self::$count . '. query | ' . $sqlTotalTime . ' ' . A::t('core', 'sec') . '. | ' . A::t('core', 'total') . ': ' . (($result) ? $result : '0 (error )') . ' ', $this->_query);
- }
-
- return $result;
- }
-
- /**
- * Creates a DB command for execution
- * @param mixed $query
- * @return CDbCommand
- */
- public function createCommand($query = null)
- {
- return new CDbCommand($this, $query);
- }
-
- /**
- * Performs a show tables query
- * @return mixed
- */
- public function showTables()
- {
- if (APPHP_MODE == 'debug') {
- $startTime = CTime::getMicrotime();
- }
-
- switch ($this->_dbDriver) {
- case 'mssql';
- case 'sqlsrv':
- $sql = "SELECT * FROM sys.all_objects WHERE type = 'U'";
- break;
- case 'pgsql':
- $sql = 'SELECT tablename FROM pg_tables WHERE tableowner = current_user';
- break;
- case 'sqlite':
- $sql = "SELECT * FROM sqlite_master WHERE type='table'";
- break;
- case 'oci':
- $sql = 'SELECT * FROM system.tab';
- break;
- case 'ibm':
- $sql = "SELECT TABLE_NAME FROM qsys2.systables" . ((CConfig::get('db.schema') != '') ? " WHERE TABLE_SCHEMA = '" . CConfig::get('db.schema') . "'" : '');
- break;
- case 'mysql':
- default:
- $sql = 'SHOW TABLES IN ' . $this->_quotes($this->_dbName);
- break;
- }
-
- try {
- $sth = $this->query($sql);
- $result = $sth->fetchAll();
- } catch (PDOException $e) {
- $this->_errorLog('showTables [database.php, ln.:' . $e->getLine() . ']', $e->getMessage());
- $result = false;
- }
-
- // Save query
- $this->_query = $sql;
-
- // Save data for debug
- if (APPHP_MODE == 'debug') {
- $finishTime = CTime::getMicrotime();
- $sqlTotalTime = round((float)$finishTime - (float)$startTime, 5);
- CDebug::addSqlTime($sqlTotalTime);
- CDebug::addMessage('queries', ++self::$count . '. query | ' . $sqlTotalTime . ' ' . A::t('core', 'sec') . '. | ' . A::t('core', 'total') . ': ' . (($result) ? count($result) : '0 (error )') . ' ', $this->_query);
- }
-
- return $result;
- }
-
- /**
- * Performs a show column query
- * @param string $table
- * @return mixed
- */
- public function showColumns($table = '')
- {
- if (APPHP_MODE == 'debug') {
- $startTime = CTime::getMicrotime();
- }
-
- $cacheContent = '';
-
- switch ($this->_dbDriver) {
- case 'ibm':
- $sql = "SELECT COLUMN_NAME FROM qsys2.syscolumns WHERE TABLE_NAME = '" . $this->_dbPrefix . $table . "'" . ((CConfig::get('db.schema') != '') ? " AND TABLE_SCHEMA = '" . CConfig::get('db.schema') . "'" : '');
- break;
- case 'mssql':
- case 'sqlsrv':
- /// old version
- /// $sql = "SELECT COLUMN_NAME, data_type, character_maximum_length FROM ".$this->_dbName.".information_schema.columns WHERE table_name = '".$this->_dbPrefix.$table."'";
- $sql = "SELECT COLUMN_NAME, data_type, IS_NULLABLE, '', COLUMN_DEFAULT, character_maximum_length as extra FROM " . $this->_dbName . ".information_schema.columns WHERE table_name = '" . $this->_dbPrefix . $table . "'";
- break;
- default:
- $sql = 'SHOW COLUMNS FROM ' . $this->_quotes($this->_dbPrefix . $table);
- break;
- }
-
- try {
- if ($this->_isCacheAllowed(true)) {
- $cacheContent = CCache::getContent(
- $this->_cacheDir . md5($sql) . '.cch',
- $this->_cacheLifetime
- );
- }
-
- if (!$cacheContent) {
- $sth = $this->query($sql);
- $result = $sth->fetchAll();
-
- if ($this->_isCacheAllowed(true)) CCache::setContent($result, $this->_cacheDir);
- } else {
- $result = $cacheContent;
- }
- } catch (PDOException $e) {
- $this->_errorLog('showColumns [database.php, ln.:' . $e->getLine() . ']', $e->getMessage());
- $result = false;
- }
-
- // Save query
- $this->_query = $sql;
-
- // Save data for debug
- if (APPHP_MODE == 'debug') {
- $finishTime = CTime::getMicrotime();
- $sqlTotalTime = round((float)$finishTime - (float)$startTime, 5);
- CDebug::addSqlTime($sqlTotalTime);
- CDebug::addMessage('queries', ++self::$count . '. query | ' . $sqlTotalTime . ' ' . A::t('core', 'sec') . '. | ' . A::t('core', 'total') . ': ' . (($result) ? count($result) : '0 (error )') . ($cacheContent ? ' [cached] ' : '') . ' ', $this->_query);
- }
-
- return $result;
- }
-
- /**
- * Returns database engine version
- */
- public function getVersion()
- {
- $version = A::t('core', 'Unknown');
- if (self::$_instance != null && !empty($this->_dbName)) {
- $version = @self::getAttribute(PDO::ATTR_SERVER_VERSION);
- if (empty($version) && empty(self::$_error)) {
- $version = $this->query('select version()')->fetchColumn();
- }
- // Clean version number from alphabetic characters
- $version = preg_replace('/[^0-9,.]/', '', $version);
- }
-
- return $version;
- }
-
- /**
- * Get error status
- * @return boolean
- */
- public static function getError()
- {
- return self::$_error;
- }
-
- /**
- * Get error message
- * @return string
- */
- public static function getErrorMessage()
- {
- return self::$_errorMessage;
- }
-
- /**
- * Initialize connection
- * @param string $dbDriver
- * @param string $dbSocket
- * @param string $dbHost
- * @param string $dbPort
- * @param string $dbName
- * @param string $dbUser
- * @param string $dbPassword
- * @param string $dbCharset
- * @return void
- */
- private function _init($dbDriver = '', $dbSocket = '', $dbHost = '', $dbPort = '', $dbName = '', $dbUser = '', $dbPassword = '', $dbCharset = '')
- {
- // Set db connection type, port and db name
- if (strcasecmp($dbDriver, 'sqlsrv') == 0) {
- $dsn = 'sqlsrv:Server=' . $dbHost;
- $dsn .= !empty($dbPort) ? ',' . $dbPort : '';
- $dsn .= ';Database=' . $dbName;
-
- $this->exec("SET NAMES '" . $dbCharset . "'");
-
- @parent::__construct($dsn, $dbUser, $dbPassword, array());
-
- } else {
- $dsn = (!empty($dbSocket)) ? $dbDriver . ':unix_socket=' . $dbSocket : $dbDriver . ':host=' . $dbHost;
- $dsn .= !empty($dbPort) ? ';port=' . $dbPort : '';
- $dsn .= ';dbname=' . $dbName;
- $options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);
-
- if (version_compare(phpversion(), '5.3.6', '<')) {
- if (defined('PDO::MYSQL_ATTR_INIT_COMMAND')) {
- $options[PDO::MYSQL_ATTR_INIT_COMMAND] = "SET NAMES '" . $dbCharset . "'";
- }
- } else {
- $dsn .= ';charset=' . $dbCharset;
- }
-
- @parent::__construct($dsn, $dbUser, $dbPassword, $options);
-
- if (version_compare(phpversion(), '5.3.6', '<') && !defined('PDO::MYSQL_ATTR_INIT_COMMAND')) {
- $this->exec("SET NAMES '" . $dbCharset . "'");
- }
- }
- }
-
- /**
- * Writes error log
- * @param string $debugMessage
- * @param string $errorMessage
- */
- private function _errorLog($debugMessage, $errorMessage)
- {
- self::$_error = true;
- self::$_errorMessage = $errorMessage;
- CDebug::addMessage('errors', $debugMessage, $errorMessage, 'session');
- }
-
- /**
- * Returns fata error page content
- * @return html code
- */
- private static function _fatalErrorPageContent()
- {
- return '
+
+ /** @var string */
+ public static $count = 0;
+
+ /** @var object */
+ private static $_instance;
+ /** @var string */
+ private $_dbPrefix;
+ /** @var string */
+ private $_dbDriver;
+ /** @var string */
+ private $_dbName;
+ /** @var bool */
+ private $_cache;
+ /** @var string */
+ private $_cacheType;
+ /** @var int */
+ private $_cacheLifetime;
+ /** @var string */
+ private $_cacheDir;
+ /** @var string */
+ private $_query;
+ /** @var char */
+ private $_backQuote = '`';
+ /** @var boolean */
+ private static $_error;
+ /** @var string */
+ private static $_errorMessage;
+
+ /**
+ * Class default constructor
+ *
+ * @param array $params
+ */
+ public function __construct($params = [])
+ {
+ // For direct use (e.g. setup module)
+ if ( ! empty($params)) {
+ $dbDriver = isset($params['dbDriver']) ? $params['dbDriver'] : '';
+ $dbSocket = isset($params['dbSocket']) ? $params['dbSocket'] : '';
+ $dbHost = isset($params['dbHost']) ? $params['dbHost'] : '';
+ $dbPort = isset($params['dbPort']) ? $params['dbPort'] : '';
+ $dbName = isset($params['dbName']) ? $params['dbName'] : '';
+ $dbUser = isset($params['dbUser']) ? $params['dbUser'] : '';
+ $dbPassword = isset($params['dbPassword']) ? $params['dbPassword'] : '';
+ $dbCharset = isset($params['dbCharset']) ? $params['dbCharset'] : 'utf8';
+
+ try {
+ $this->_init($dbDriver, $dbSocket, $dbHost, $dbPort, $dbName, $dbUser, $dbPassword, $dbCharset);
+ $this->_dbDriver = $dbDriver;
+ $this->_dbName = $dbName;
+ $this->_dbPrefix = '';
+ } catch (Exception $e) {
+ self::$_error = true;
+ self::$_errorMessage = $e->getMessage();
+ }
+ } else {
+ if ( ! A::app()->isSetup()) {
+ if (APPHP_MODE == 'debug') {
+ $startTime = CTime::getMicrotime();
+ }
+
+ try {
+ if (CConfig::get('db') != '') {
+ $this->_init(
+ CConfig::get('db.driver'),
+ CConfig::get('db.socket'),
+ CConfig::get('db.host'),
+ CConfig::get('db.port'),
+ CConfig::get('db.database'),
+ CConfig::get('db.username'),
+ CConfig::get('db.password'),
+ CConfig::get('db.charset', 'utf8')
+ );
+ } else {
+ throw new Exception('Missing database configuration file');
+ }
+ } catch (Exception $e) {
+ header('HTTP/1.1 503 Service Temporarily Unavailable');
+ header('Status: 503 Service Temporarily Unavailable');
+ $output = self::_fatalErrorPageContent();
+ if (APPHP_MODE == 'debug') {
+ $output = str_ireplace(
+ '{DESCRIPTION}',
+ ''.A::t(
+ 'core',
+ 'This application is currently experiencing some database difficulties'
+ ).'
',
+ $output
+ );
+ $output = str_ireplace(
+ '{CODE}',
+ 'Description: '.$e->getMessage().'
+ File: '.$e->getFile().'
+ Line: '.$e->getLine(),
+ $output
+ );
+ } else {
+ $output = str_ireplace(
+ '{DESCRIPTION}',
+ ''.A::t(
+ 'core',
+ 'This application is currently experiencing some database difficulties. Please check back again later'
+ ).'
',
+ $output
+ );
+ $output = str_ireplace(
+ '{CODE}',
+ A::t(
+ 'core',
+ 'For more information turn on debug mode in your application'
+ ),
+ $output
+ );
+ }
+ echo $output;
+ exit(1);
+ }
+
+ $this->_dbDriver = CConfig::get('db.driver');
+ $this->_dbName = CConfig::get('db.database');
+ $this->_dbPrefix = CConfig::get('db.prefix');
+
+ $this->_cache = CConfig::get('cache.db.enable') ? true : false;
+ $this->_cacheType = in_array(CConfig::get('cache.db.type'), ['auto', 'manual']) ? CConfig::get(
+ 'cache.db.type'
+ ) : 'auto';
+ $this->_cacheLifetime = CConfig::get('cache.db.lifetime', 0); /* in minutes */
+ $this->_cacheDir = CConfig::get('cache.db.path'); /* protected/tmp/cache/ */
+ if ($this->_cache) {
+ CDebug::addMessage('general', 'cache', 'enabled ('.$this->_cacheType.') ');
+ }
+
+ // Save data for debug
+ if (APPHP_MODE == 'debug') {
+ $finishTime = CTime::getMicrotime();
+ $sqlConnectionTime = round((float)$finishTime - (float)$startTime, 5);
+ CDebug::addSqlConnectionTime($sqlConnectionTime);
+ }
+ }
+ }
+
+ // Set back quote according to database driver
+ if ( ! empty($this->_dbDriver) && preg_match('/mssql|sqlsrv/i', $this->_dbDriver)) {
+ $this->_backQuote = '';
+ }
+ }
+
+ /**
+ * Initializes the database class
+ *
+ * @param array $params
+ */
+ public static function init($params = [])
+ {
+ if (self::$_instance == null) {
+ self::$_instance = new self($params);
+ }
+
+ return self::$_instance;
+ }
+
+ /**
+ * Sets cache off
+ */
+ public function cacheOn()
+ {
+ $this->_setCaching(true);
+ }
+
+ /**
+ * Sets cache off
+ */
+ public function cacheOff()
+ {
+ $this->_setCaching(false);
+ }
+
+ /**
+ * Performs select query
+ *
+ * @param string $sql SQL string
+ * @param array $params parameters to bind
+ * @param string $method (e.g 'fetch' or 'fetchAll')
+ * @param constant $fetchMode PDO fetch mode
+ * @param bool|string $cacheId cache identification
+ *
+ * @return mixed - an array containing all of the result set rows
+ * Ex.: Array([0] => Array([id] => 11, [name] => John), ...)
+ */
+ public function select($sql, $params = [], $method = 'fetchAll', $fetchMode = PDO::FETCH_ASSOC, $cacheId = '')
+ {
+ if (APPHP_MODE == 'debug') {
+ $startTime = CTime::getMicrotime();
+ }
+
+ $sth = $this->prepare($sql);
+ $cacheContent = null;
+ $error = false;
+
+ try {
+ if ($this->_isCacheAllowed($cacheId)) {
+ $cacheFile = ! empty($cacheId) ? $cacheId : $sql.(is_array($params) ? implode('|', $params) : '');
+ $cacheContent = CCache::getContent(
+ $this->_cacheDir.md5($cacheFile).'.cch',
+ $this->_cacheLifetime
+ );
+ }
+
+ if ( ! $cacheContent) {
+ if (is_array($params)) {
+ foreach ($params as $key => $value) {
+ if (is_array($value)) {
+ continue;
+ }
+ [$key, $param] = $this->_prepareParams($key);
+ $sth->bindValue($key, $value, $param);
+ }
+ }
+ $sth->execute();
+ $result = $sth->$method($fetchMode);
+
+ if ($this->_isCacheAllowed($cacheId)) {
+ CCache::setContent($result, $this->_cacheDir);
+ }
+ } else {
+ $result = $cacheContent;
+ }
+ } catch (PDOException $e) {
+ $this->_errorLog(
+ 'select [database.php, ln.:'.$e->getLine().']',
+ $e->getMessage().' => '.$this->_interpolateQuery($sql, $params)
+ );
+ $result = false;
+ $error = true;
+ }
+
+ // Interpolate query and save it
+ $this->_query = $this->_interpolateQuery($sql, $params);
+
+ // Save data for debug
+ if (APPHP_MODE == 'debug') {
+ $finishTime = CTime::getMicrotime();
+ $sqlTotalTime = round((float)$finishTime - (float)$startTime, 5);
+ CDebug::addSqlTime($sqlTotalTime);
+ CDebug::addMessage(
+ 'queries',
+ ''.++self::$count.'. select | '.$sqlTotalTime.' '.A::t(
+ 'core',
+ 'sec'
+ ).'. | '.A::t('core', 'total').': '.(($result) ? count($result)
+ : '0 ('.($error ? 'error' : 'empty').' )').($cacheContent ? ' [cached] ' : '').' ',
+ $this->_query
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Performs insert query
+ *
+ * @param string $table name of the table to insert into
+ * @param array $data associative array
+ * @param bool $forceUpdate used to force update on Demo mode
+ *
+ * @return int|boolean
+ */
+ public function insert($table, $data, $forceUpdate = false)
+ {
+ if (APPHP_MODE == 'demo' && ! $forceUpdate) {
+ self::$_errorMessage = A::t('core', 'This operation is blocked in Demo Mode!');
+
+ return false;
+ }
+
+ if (APPHP_MODE == 'debug') {
+ $startTime = CTime::getMicrotime();
+ }
+
+ ksort($data);
+
+ $fieldNames = $this->_quotes(implode($this->_backQuote.', '.$this->_backQuote, array_keys($data)));
+
+ $fieldValues = '';
+ if (is_array($data)) {
+ foreach ($data as $key => $value) {
+ // Regular fields: :last_name,
+ // Encrypted fields: AES_ENCRYPT(:last_name, "key"),
+ // Use str_replace('('.$key.',', '(:'.$key.',', ... for encrypted fields
+ $fieldValues .= (is_array($value) ? str_replace('('.$key.',', '(:'.$key.',', $value['param_key'])
+ : ':'.$key).',';
+ }
+ $fieldValues = rtrim($fieldValues, ',');
+ }
+
+ $sql = 'INSERT INTO '.$this->_quotes($this->_dbPrefix.$table).' ('.$fieldNames.') VALUES ('.$fieldValues.')';
+ $sth = $this->prepare($sql);
+
+ if (is_array($data)) {
+ foreach ($data as $key => $value) {
+ [$key, $param] = $this->_prepareParams($key);
+ $sth->bindValue(':'.$key, (is_array($value) ? $value['param_value'] : $value), $param);
+ }
+ }
+
+ try {
+ $sth->execute();
+ $result = $this->lastInsertId();
+ } catch (PDOException $e) {
+ $this->_errorLog(
+ 'insert [database.php, ln.:'.$e->getLine().']',
+ $e->getMessage().' => '.$this->_interpolateQuery($sql, $data)
+ );
+ $result = false;
+ }
+
+ // Interpolate query and save it
+ $this->_query = $this->_interpolateQuery($sql, $data);
+
+ // Save data for debug
+ if (APPHP_MODE == 'debug') {
+ $finishTime = CTime::getMicrotime();
+ $sqlTotalTime = round((float)$finishTime - (float)$startTime, 5);
+ CDebug::addSqlTime($sqlTotalTime);
+ CDebug::addMessage(
+ 'queries',
+ ''.++self::$count.'. insert | '.$sqlTotalTime.' '.A::t(
+ 'core',
+ 'sec'
+ ).'. | ID: '.(($result) ? $result : '0 (error )').' ',
+ $this->_query
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Performs update query
+ *
+ * @param string $table name of table to update
+ * @param array $data an associative array
+ * @param string $where the WHERE clause of query
+ * @param array $params , ex.: array('is_default'=>0, 'rate'=>array('expression'=>'ROUND(rate/1.2,4)'))
+ * @param bool $forceUpdate used to force update on Demo mode
+ * @param boolean
+ */
+ public function update($table, $data, $where = '1', $params = [], $forceUpdate = false)
+ {
+ if (APPHP_MODE == 'demo' && ! $forceUpdate) {
+ self::$_errorMessage = A::t('core', 'This operation is blocked in Demo Mode!');
+
+ return false;
+ }
+
+ if (APPHP_MODE == 'debug') {
+ $startTime = CTime::getMicrotime();
+ }
+
+ ksort($data);
+
+ $fieldDetails = null;
+ if (is_array($data)) {
+ foreach ($data as $key => $value) {
+ // Regular fields: `last_name` = :last_name, : 'last_name'=>'John',
+ // Expression: 'rate'=>'ROUND(rate/'.$this->rate.')' : 'rate'=>array('expression'=>'ROUND(rate/'.$this->rate.',4)',
+ // Encrypted fields: 'last_name'=>'AES_ENCRYPT(:last_name, "key")' : `last_name` = AES_ENCRYPT(:last_name, "key"),
+ // Use str_replace('('.$key.',', '(:'.$key.',', ... for encrypted fields
+ if (isset($value['expression'])) {
+ $fieldDetails .= $this->_quotes($key).' = '.$value['expression'].',';
+ } else {
+ $fieldDetails .= $this->_quotes($key).' = '.(is_array($value) ? str_replace(
+ '('.$key.',',
+ '(:'.$key.',',
+ $value['param_key']
+ ) : ':'.$key).',';
+ }
+ }
+ }
+ $fieldDetails = rtrim($fieldDetails, ',');
+ $sql = 'UPDATE '.$this->_quotes($this->_dbPrefix.$table).' SET '.$fieldDetails.' WHERE '.$where;
+
+ $sth = $this->prepare($sql);
+ if (is_array($data)) {
+ foreach ($data as $key => $value) {
+ if ( ! isset($value['expression'])) {
+ [$key, $param] = $this->_prepareParams($key);
+ $sth->bindValue(':'.$key, (is_array($value) ? $value['param_value'] : $value), $param);
+ }
+ }
+ }
+ if (is_array($params)) {
+ foreach ($params as $key => $value) {
+ [$key, $param] = $this->_prepareParams($key);
+ $sth->bindValue($key, $value, $param);
+ }
+ }
+
+ try {
+ $sth->execute();
+ // $result = $sth->rowCount();
+ $result = true;
+ } catch (PDOException $e) {
+ // Get trace from parent level
+ // $trace = $e->getTrace();
+ // echo '';
+ // echo $trace[1]['file'];
+ // echo $trace[1]['line'];
+ // echo ' ';
+ $this->_errorLog(
+ 'update [database.php, ln.:'.$e->getLine().']',
+ $e->getMessage().' => '.$this->_interpolateQuery($sql, $data)
+ );
+ $result = false;
+ }
+
+ // Interpolate query and save it
+ $this->_query = $this->_interpolateQuery($sql, $data);
+
+ // Save data for debug
+ if (APPHP_MODE == 'debug') {
+ $finishTime = CTime::getMicrotime();
+ $sqlTotalTime = round((float)$finishTime - (float)$startTime, 5);
+ CDebug::addSqlTime($sqlTotalTime);
+ CDebug::addMessage(
+ 'queries',
+ ''.++self::$count.'. update | '.$sqlTotalTime.' '.A::t(
+ 'core',
+ 'sec'
+ ).'. | '.A::t('core', 'total').': '.(($result) ? $sth->rowCount() : '0 (error )').' ',
+ $this->_query
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Performs delete query
+ *
+ * @param string $table
+ * @param string $where the WHERE clause of query
+ * @param array $params
+ *
+ * @return bool|int affected rows or false
+ */
+ public function delete($table, $where = '', $params = [])
+ {
+ if (APPHP_MODE == 'demo') {
+ self::$_errorMessage = A::t('core', 'This operation is blocked in Demo Mode!');
+
+ return false;
+ }
+
+ if (APPHP_MODE == 'debug') {
+ $startTime = CTime::getMicrotime();
+ }
+
+ $where_clause = ( ! empty($where) && ! preg_match('/\bwhere\b/i', $where)) ? ' WHERE '.$where : $where;
+ $sql = 'DELETE FROM '.$this->_quotes($this->_dbPrefix.$table).' '.$where_clause;
+
+ $sth = $this->prepare($sql);
+ if (is_array($params)) {
+ foreach ($params as $key => $value) {
+ [$key, $param] = $this->_prepareParams($key);
+ $sth->bindValue($key, $value, $param);
+ }
+ }
+
+ try {
+ $sth->execute();
+ $result = $sth->rowCount();
+ } catch (PDOException $e) {
+ $this->_errorLog(
+ 'delete [database.php, ln.:'.$e->getLine().']',
+ $e->getMessage().' => '.$this->_interpolateQuery($sql, $params)
+ );
+ $result = false;
+ }
+
+ // Interpolate query and save it
+ $this->_query = $this->_interpolateQuery($sql, $params);
+
+ // Save data for debug
+ if (APPHP_MODE == 'debug') {
+ $finishTime = CTime::getMicrotime();
+ $sqlTotalTime = round((float)$finishTime - (float)$startTime, 5);
+ CDebug::addSqlTime($sqlTotalTime);
+ CDebug::addMessage(
+ 'queries',
+ ''.++self::$count.'. delete | '.$sqlTotalTime.' '.A::t(
+ 'core',
+ 'sec'
+ ).'. | '.A::t('core', 'total').': '.(($result) ? $result : '0 (warning )').' ',
+ $this->_query
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Builds and executes a SQL statement for truncating a DB table
+ *
+ * @param string $table the table to be truncated
+ *
+ * @return bool|int affected rows or false
+ * @since 1.1.0
+ */
+ public function truncate($table)
+ {
+ if (APPHP_MODE == 'demo') {
+ self::$_errorMessage = A::t('core', 'This operation is blocked in Demo Mode!');
+
+ return false;
+ }
+
+ if (APPHP_MODE == 'debug') {
+ $startTime = CTime::getMicrotime();
+ }
+
+ $sql = ' TRUNCATE TABLE '.$this->_quotes($this->_dbPrefix.$table);
+ $sth = $this->prepare($sql);
+
+ try {
+ $sth->execute();
+ $result = $sth->rowCount();
+ } catch (PDOException $e) {
+ $this->_errorLog('delete [database.php, ln.:'.$e->getLine().']', $e->getMessage().' => '.$sql);
+ $result = false;
+ }
+
+ // Save query
+ $this->_query = $sql;
+
+ // Save data for debug
+ if (APPHP_MODE == 'debug') {
+ $finishTime = CTime::getMicrotime();
+ $sqlTotalTime = round((float)$finishTime - (float)$startTime, 5);
+ CDebug::addSqlTime($sqlTotalTime);
+ CDebug::addMessage(
+ 'queries',
+ ''.++self::$count.'. truncate | '.$sqlTotalTime.' '.A::t(
+ 'core',
+ 'sec'
+ ).'. | '.A::t('core', 'total').': '.(($result) ? $result : '0 (warning )').' ',
+ $this->_query
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns ID of the last inserted record
+ *
+ * @return int
+ */
+ public function lastId()
+ {
+ return ( ! empty($this)) ? $this->lastInsertId() : 0;
+ }
+
+ /**
+ * Returns last query
+ *
+ * @return string
+ */
+ public function lastQuery()
+ {
+ return $this->_query;
+ }
+
+ /**
+ * Performs a standard query
+ *
+ * @param string $sql
+ * @param array $params
+ * @param constant $fetchMode PDO fetch mode
+ *
+ * @return mixed - an array containing all of the result set rows
+ */
+ public function customQuery($sql, $params = [], $fetchMode = PDO::FETCH_ASSOC)
+ {
+ if (APPHP_MODE == 'demo') {
+ self::$_errorMessage = A::t('core', 'This operation is blocked in Demo Mode!');
+
+ return false;
+ }
+
+ if (APPHP_MODE == 'debug') {
+ $startTime = CTime::getMicrotime();
+ }
+
+ try {
+ if (is_array($params) && ! empty($params)) {
+ $sth = $this->prepare($sql);
+ foreach ($params as $key => $value) {
+ [$key, $param] = $this->_prepareParams($key);
+ $sth->bindValue($key, $value, $param);
+ }
+ $sth->execute();
+ } else {
+ $sth = $this->query($sql);
+ }
+ $result = $sth->fetchAll($fetchMode);
+ } catch (PDOException $e) {
+ $this->_errorLog('customQuery [database.php, ln.:'.$e->getLine().']', $e->getMessage().' => '.$sql);
+ $result = false;
+ }
+
+ // Interpolate query and save it
+ $this->_query = $this->_interpolateQuery($sql, $params);
+
+ // Save data for debug
+ if (APPHP_MODE == 'debug') {
+ $finishTime = CTime::getMicrotime();
+ $sqlTotalTime = round((float)$finishTime - (float)$startTime, 5);
+ CDebug::addSqlTime($sqlTotalTime);
+ CDebug::addMessage(
+ 'queries',
+ ''.++self::$count.'. query | '.$sqlTotalTime.' '.A::t(
+ 'core',
+ 'sec'
+ ).'. | '.A::t('core', 'total').': '.(($result) ? count($result) : '0 (error )').' ',
+ $this->_query
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Performs a standard exec
+ *
+ * @param string $sql
+ * @param array $params
+ * @param bool $forceUpdate used to force update on Demo mode
+ *
+ * @return boolean
+ */
+ public function customExec($sql, $params = [], $forceUpdate = false)
+ {
+ if (APPHP_MODE == 'demo' && ! $forceUpdate) {
+ self::$_errorMessage = A::t('core', 'This operation is blocked in Demo Mode!');
+
+ return false;
+ }
+
+ if (APPHP_MODE == 'debug') {
+ $startTime = CTime::getMicrotime();
+ }
+
+ try {
+ if (is_array($params) && ! empty($params)) {
+ $sth = $this->prepare($sql);
+ foreach ($params as $key => $value) {
+ [$key, $param] = $this->_prepareParams($key);
+ $sth->bindValue($key, $value, $param);
+ }
+ $sth->execute();
+ $result = $sth->rowCount();
+ } else {
+ $result = $this->exec($sql);
+ }
+ } catch (PDOException $e) {
+ $this->_errorLog('customExec [database.php, ln.:'.$e->getLine().']', $e->getMessage().' => '.$sql);
+ $result = false;
+ }
+
+ // Interpolate query and save it
+ $this->_query = $this->_interpolateQuery($sql, $params);
+
+ // Save data for debug
+ if (APPHP_MODE == 'debug') {
+ $finishTime = CTime::getMicrotime();
+ $sqlTotalTime = round((float)$finishTime - (float)$startTime, 5);
+ CDebug::addSqlTime($sqlTotalTime);
+ CDebug::addMessage(
+ 'queries',
+ ''.++self::$count.'. query | '.$sqlTotalTime.' '.A::t(
+ 'core',
+ 'sec'
+ ).'. | '.A::t('core', 'total').': '.(($result) ? $result : '0 (error )').' ',
+ $this->_query
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Creates a DB command for execution
+ *
+ * @param mixed $query
+ *
+ * @return CDbCommand
+ */
+ public function createCommand($query = null)
+ {
+ return new CDbCommand($this, $query);
+ }
+
+ /**
+ * Performs a show tables query
+ *
+ * @return mixed
+ */
+ public function showTables()
+ {
+ if (APPHP_MODE == 'debug') {
+ $startTime = CTime::getMicrotime();
+ }
+
+ switch ($this->_dbDriver) {
+ case 'mssql';
+ case 'sqlsrv':
+ $sql = "SELECT * FROM sys.all_objects WHERE type = 'U'";
+ break;
+ case 'pgsql':
+ $sql = 'SELECT tablename FROM pg_tables WHERE tableowner = current_user';
+ break;
+ case 'sqlite':
+ $sql = "SELECT * FROM sqlite_master WHERE type='table'";
+ break;
+ case 'oci':
+ $sql = 'SELECT * FROM system.tab';
+ break;
+ case 'ibm':
+ $sql = "SELECT TABLE_NAME FROM qsys2.systables".((CConfig::get('db.schema') != '')
+ ? " WHERE TABLE_SCHEMA = '".CConfig::get('db.schema')."'" : '');
+ break;
+ case 'mysql':
+ default:
+ $sql = 'SHOW TABLES IN '.$this->_quotes($this->_dbName);
+ break;
+ }
+
+ try {
+ $sth = $this->query($sql);
+ $result = $sth->fetchAll();
+ } catch (PDOException $e) {
+ $this->_errorLog('showTables [database.php, ln.:'.$e->getLine().']', $e->getMessage());
+ $result = false;
+ }
+
+ // Save query
+ $this->_query = $sql;
+
+ // Save data for debug
+ if (APPHP_MODE == 'debug') {
+ $finishTime = CTime::getMicrotime();
+ $sqlTotalTime = round((float)$finishTime - (float)$startTime, 5);
+ CDebug::addSqlTime($sqlTotalTime);
+ CDebug::addMessage(
+ 'queries',
+ ''.++self::$count.'. query | '.$sqlTotalTime.' '.A::t(
+ 'core',
+ 'sec'
+ ).'. | '.A::t('core', 'total').': '.(($result) ? count($result) : '0 (error )').' ',
+ $this->_query
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Performs a show column query
+ *
+ * @param string $table
+ *
+ * @return mixed
+ */
+ public function showColumns($table = '')
+ {
+ if (APPHP_MODE == 'debug') {
+ $startTime = CTime::getMicrotime();
+ }
+
+ $cacheContent = '';
+
+ switch ($this->_dbDriver) {
+ case 'ibm':
+ $sql = "SELECT COLUMN_NAME FROM qsys2.syscolumns WHERE TABLE_NAME = '".$this->_dbPrefix.$table."'"
+ .((CConfig::get('db.schema') != '') ? " AND TABLE_SCHEMA = '".CConfig::get('db.schema')."'" : '');
+ break;
+ case 'mssql':
+ case 'sqlsrv':
+ /// old version
+ /// $sql = "SELECT COLUMN_NAME, data_type, character_maximum_length FROM ".$this->_dbName.".information_schema.columns WHERE table_name = '".$this->_dbPrefix.$table."'";
+ $sql
+ = "SELECT COLUMN_NAME, data_type, IS_NULLABLE, '', COLUMN_DEFAULT, character_maximum_length as extra FROM "
+ .$this->_dbName.".information_schema.columns WHERE table_name = '".$this->_dbPrefix.$table."'";
+ break;
+ default:
+ $sql = 'SHOW COLUMNS FROM '.$this->_quotes($this->_dbPrefix.$table);
+ break;
+ }
+
+ try {
+ if ($this->_isCacheAllowed(true)) {
+ $cacheContent = CCache::getContent(
+ $this->_cacheDir.md5($sql).'.cch',
+ $this->_cacheLifetime
+ );
+ }
+
+ if ( ! $cacheContent) {
+ $sth = $this->query($sql);
+ $result = $sth->fetchAll();
+
+ if ($this->_isCacheAllowed(true)) {
+ CCache::setContent($result, $this->_cacheDir);
+ }
+ } else {
+ $result = $cacheContent;
+ }
+ } catch (PDOException $e) {
+ $this->_errorLog('showColumns [database.php, ln.:'.$e->getLine().']', $e->getMessage());
+ $result = false;
+ }
+
+ // Save query
+ $this->_query = $sql;
+
+ // Save data for debug
+ if (APPHP_MODE == 'debug') {
+ $finishTime = CTime::getMicrotime();
+ $sqlTotalTime = round((float)$finishTime - (float)$startTime, 5);
+ CDebug::addSqlTime($sqlTotalTime);
+ CDebug::addMessage(
+ 'queries',
+ ''.++self::$count.' show | '.$sqlTotalTime.' '.A::t(
+ 'core',
+ 'sec'
+ ).'. | '.A::t('core', 'total').': '.(($result) ? count($result) : '0 (error )').($cacheContent
+ ? ' [cached] ' : '').' ',
+ $this->_query
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns database engine version
+ */
+ public function getVersion()
+ {
+ $version = A::t('core', 'Unknown');
+ if (self::$_instance != null && ! empty($this->_dbName)) {
+ $version = @self::getAttribute(PDO::ATTR_SERVER_VERSION);
+ if (empty($version) && empty(self::$_error)) {
+ $version = $this->query('select version()')->fetchColumn();
+ }
+ // Clean version number from alphabetic characters
+ $version = preg_replace('/[^0-9,.]/', '', $version);
+ }
+
+ return $version;
+ }
+
+ /**
+ * Get error status
+ *
+ * @return boolean
+ */
+ public static function getError()
+ {
+ return self::$_error;
+ }
+
+ /**
+ * Get error message
+ *
+ * @return string
+ */
+ public static function getErrorMessage()
+ {
+ return self::$_errorMessage;
+ }
+
+ /**
+ * Initialize connection
+ *
+ * @param string $dbDriver
+ * @param string $dbSocket
+ * @param string $dbHost
+ * @param string $dbPort
+ * @param string $dbName
+ * @param string $dbUser
+ * @param string $dbPassword
+ * @param string $dbCharset
+ *
+ * @return void
+ */
+ private function _init(
+ $dbDriver = '',
+ $dbSocket = '',
+ $dbHost = '',
+ $dbPort = '',
+ $dbName = '',
+ $dbUser = '',
+ $dbPassword = '',
+ $dbCharset = ''
+ ) {
+ // Set db connection type, port and db name
+ if (strcasecmp($dbDriver, 'sqlsrv') == 0) {
+ $dsn = 'sqlsrv:Server='.$dbHost;
+ $dsn .= ! empty($dbPort) ? ','.$dbPort : '';
+ $dsn .= ';Database='.$dbName;
+
+ $this->exec("SET NAMES '".$dbCharset."'");
+
+ @parent::__construct($dsn, $dbUser, $dbPassword, []);
+ } else {
+ $dsn = ( ! empty($dbSocket)) ? $dbDriver.':unix_socket='.$dbSocket : $dbDriver.':host='.$dbHost;
+ $dsn .= ! empty($dbPort) ? ';port='.$dbPort : '';
+ $dsn .= ';dbname='.$dbName;
+ $options = [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION];
+
+ if (version_compare(phpversion(), '5.3.6', '<')) {
+ if (defined('PDO::MYSQL_ATTR_INIT_COMMAND')) {
+ $options[PDO::MYSQL_ATTR_INIT_COMMAND] = "SET NAMES '".$dbCharset."'";
+ }
+ } else {
+ $dsn .= ';charset='.$dbCharset;
+ }
+
+ @parent::__construct($dsn, $dbUser, $dbPassword, $options);
+
+ if (version_compare(phpversion(), '5.3.6', '<') && ! defined('PDO::MYSQL_ATTR_INIT_COMMAND')) {
+ $this->exec("SET NAMES '".$dbCharset."'");
+ }
+ }
+ }
+
+ /**
+ * Writes error log
+ *
+ * @param string $debugMessage
+ * @param string $errorMessage
+ */
+ private function _errorLog($debugMessage, $errorMessage)
+ {
+ self::$_error = true;
+ self::$_errorMessage = $errorMessage;
+ CDebug::addMessage('errors', $debugMessage, $errorMessage, 'session');
+ }
+
+ /**
+ * Returns fata error page content
+ *
+ * @return html code
+ */
+ private static function _fatalErrorPageContent()
+ {
+ return '
@@ -858,121 +1028,133 @@ private static function _fatalErrorPageContent()
';
- }
-
- /**
- * Replaces any parameter placeholders in a query with the value of that parameter
- * @param string $sql
- * @param array $params
- * @return string
- */
- private function _interpolateQuery($sql, $params = array())
- {
- $keys = array();
- $count = 0;
- if (!is_array($params)) return $sql;
-
- // Build regular expression for each parameter
- foreach ($params as $key => $value) {
- if (is_string($key)) {
- $ind = strpos($key, ':');
- if ($ind == 1) {
- // used param with prefix, like: i:param, f:param etc.
- $newKey = substr($key, 2, strlen($key));
- $keys[] = '/:' . $newKey . '/';
- $params[$newKey] = $params[$key];
- unset($params[$key]);
- } elseif ($ind !== false) {
- $keys[] = '/' . $key . '/';
- } else {
- $keys[] = '/:' . $key . '/';
- }
- } else {
- $keys[] = '/[?]/';
- }
-
- if (is_array($value)) {
- if (isset($value['expression'])) {
- $params[$key] = $value['expression'];
- } elseif (isset($value['param_key'])) {
- // Show encrypted fields
- $params[$key] = str_replace($key, $value['param_value'], $value['param_key']);
- }
- } else {
- // Show regular fields
- $params[$key] = "'$value'";
- }
- }
-
- return preg_replace($keys, $params, $sql, 1, $count);
- }
-
- /**
- * Prepares/changes keys and parameters
- * @param $key
- * @return array
- */
- private function _prepareParams($key)
- {
- $param = 0;
- $prefix = substr($key, 0, 2);
- switch ($prefix) {
- case 'i:':
- $key = str_replace('i:', ':', $key);
- $param = PDO::PARAM_INT;
- break;
- case 'b:':
- $key = str_replace('b:', ':', $key);
- $param = PDO::PARAM_BOOL;
- break;
- case 'f:':
- $key = str_replace('f:', ':', $key);
- $param = PDO::PARAM_STR;
- break;
- case 's:':
- $key = str_replace('s:', ':', $key);
- $param = PDO::PARAM_STR;
- break;
- case 'n:':
- $key = str_replace('n:', ':', $key);
- $param = PDO::PARAM_NULL;
- break;
- default:
- $param = PDO::PARAM_STR;
- break;
- }
- return array($key, $param);
- }
-
- /**
- * Sets cache state
- * @param bool $enabled
- */
- private function _setCaching($enabled)
- {
- $this->_cache = $this->_isCacheAllowed($enabled);
- ///if(!$this->_cache) CDebug::addMessage('general', 'cache', 'disabled');
- }
-
- /**
- * Check cache state
- * @param $cacheId
- * @return bool
- */
- private function _isCacheAllowed($cacheId = false)
- {
- return ($this->_cache && ($this->_cacheType == 'auto' || ($this->_cacheType == 'manual' && !empty($cacheId))));
- }
-
- /**
- * Escapes given string with backquotes
- * Prepares table name for using in SQL statements
- * @param string $string
- * @return string
- */
- private function _quotes($string = '')
- {
- return $this->_backQuote . $string . $this->_backQuote;
- }
-
+ }
+
+ /**
+ * Replaces any parameter placeholders in a query with the value of that parameter
+ *
+ * @param string $sql
+ * @param array $params
+ *
+ * @return string
+ */
+ private function _interpolateQuery($sql, $params = [])
+ {
+ $keys = [];
+ $count = 0;
+ if ( ! is_array($params)) {
+ return $sql;
+ }
+
+ // Build regular expression for each parameter
+ foreach ($params as $key => $value) {
+ if (is_string($key)) {
+ $ind = strpos($key, ':');
+ if ($ind == 1) {
+ // used param with prefix, like: i:param, f:param etc.
+ $newKey = substr($key, 2, strlen($key));
+ $keys[] = '/:'.$newKey.'/';
+ $params[$newKey] = $params[$key];
+ unset($params[$key]);
+ } elseif ($ind !== false) {
+ $keys[] = '/'.$key.'/';
+ } else {
+ $keys[] = '/:'.$key.'/';
+ }
+ } else {
+ $keys[] = '/[?]/';
+ }
+
+ if (is_array($value)) {
+ if (isset($value['expression'])) {
+ $params[$key] = $value['expression'];
+ } elseif (isset($value['param_key'])) {
+ // Show encrypted fields
+ $params[$key] = str_replace($key, $value['param_value'], $value['param_key']);
+ }
+ } else {
+ // Show regular fields
+ $params[$key] = "'$value'";
+ }
+ }
+
+ return preg_replace($keys, $params, $sql, 1, $count);
+ }
+
+ /**
+ * Prepares/changes keys and parameters
+ *
+ * @param $key
+ *
+ * @return array
+ */
+ private function _prepareParams($key)
+ {
+ $param = 0;
+ $prefix = substr($key, 0, 2);
+ switch ($prefix) {
+ case 'i:':
+ $key = str_replace('i:', ':', $key);
+ $param = PDO::PARAM_INT;
+ break;
+ case 'b:':
+ $key = str_replace('b:', ':', $key);
+ $param = PDO::PARAM_BOOL;
+ break;
+ case 'f:':
+ $key = str_replace('f:', ':', $key);
+ $param = PDO::PARAM_STR;
+ break;
+ case 's:':
+ $key = str_replace('s:', ':', $key);
+ $param = PDO::PARAM_STR;
+ break;
+ case 'n:':
+ $key = str_replace('n:', ':', $key);
+ $param = PDO::PARAM_NULL;
+ break;
+ default:
+ $param = PDO::PARAM_STR;
+ break;
+ }
+
+ return [$key, $param];
+ }
+
+ /**
+ * Sets cache state
+ *
+ * @param bool $enabled
+ */
+ private function _setCaching($enabled)
+ {
+ $this->_cache = $this->_isCacheAllowed($enabled);
+ ///if(!$this->_cache) CDebug::addMessage('general', 'cache', 'disabled');
+ }
+
+ /**
+ * Check cache state
+ *
+ * @param $cacheId
+ *
+ * @return bool
+ */
+ private function _isCacheAllowed($cacheId = false)
+ {
+ return ($this->_cache && ($this->_cacheType == 'auto' || ($this->_cacheType == 'manual' && ! empty($cacheId))));
+ }
+
+ /**
+ * Escapes given string with backquotes
+ * Prepares table name for using in SQL statements
+ *
+ * @param string $string
+ *
+ * @return string
+ */
+ private function _quotes($string = '')
+ {
+ return $this->_backQuote.$string.$this->_backQuote;
+ }
+
}
diff --git a/framework/db/CDbCommand.php b/framework/db/CDbCommand.php
index 89ae81f..3b713d7 100644
--- a/framework/db/CDbCommand.php
+++ b/framework/db/CDbCommand.php
@@ -19,7 +19,7 @@
* $account = CDatabase::init()->createCommand()
* ->select('id, username, password')
* ->from(CConfig::get('db.prefix').'accounts')
- * ->where('id=:id', array(':id'=>1))
+ * ->where('id=:id', [':id'=>1])
* ->queryRow();
*
*
@@ -71,757 +71,843 @@
class CDbCommand
{
- /** @var CDatabase */
- protected $_db;
- /** @var */
- protected $_dbDriver = '';
- /** @var array */
- public $_params = array();
-
- /** @var string */
- private $_text;
- /** @var string */
- private $_statement;
- /** @var string */
- private $_query;
- /** @var char */
- private $_backQuote = '`';
- /** @var int */
- private $_fetchMode = PDO::FETCH_ASSOC;
-
-
- /**
- * Class constructor
- * @param CDatabase $dbConnection
- */
- public function __construct($dbConnection = null)
- {
- $this->_db = $dbConnection;
- $this->_dbDriver = CConfig::get('db.driver');
-
- // Set back quote according to database driver
- if (preg_match('/mssql|sqlsrv/i', CConfig::get('db.driver'))) {
- $this->_backQuote = '';
- }
- }
-
- /**
- * Cleans up the command for building a new query
- * @return CDbCommand command instance
- */
- public function reset()
- {
- $this->_text = null;
- $this->_query = null;
- $this->_statement = null;
- $this->_params = array();
- return $this;
- }
-
- /**
- * Cancels execution of the SQL statement
- */
- public function cancel()
- {
- $this->_statement = null;
- }
-
- /**
- * Defines SQL statement to be executed
- * @return CDbCommand command instance
- */
- public function setText($value)
- {
- $this->_text = $value;
- $this->cancel();
- return $this;
- }
-
- /**
- * Returns SQL to be executed
- * @return string
- */
- public function getText()
- {
- if ($this->_text == '' && !empty($this->_query)) {
- $this->setText($this->buildQuery($this->_query));
- }
-
- return $this->_text;
- }
-
- /**
- * Executes the SQL statement and returns all rows
- * @param boolean $fetchAssociative
- * @param array $params
- * @return array
- */
- public function queryAll($fetchAssociative = true, $params = array())
- {
- return $this->_queryInternal('fetchAll', $fetchAssociative ? $this->_fetchMode : PDO::FETCH_NUM, $params);
- }
-
- /**
- * Executes the SQL statement and returns the first row of the result.
- * @param boolean $fetchAssociative
- * @param array $params
- * @return array
- */
- public function queryRow($fetchAssociative = true, $params = array())
- {
- return $this->_queryInternal('fetch', $fetchAssociative ? $this->_fetchMode : PDO::FETCH_NUM, $params);
- }
-
- /**
- * Executes the SQL statement and returns the value of the first column in the first row of data
- * @param array $params
- * @return array
- */
- public function queryScalar($params = array())
- {
- return $this->_queryInternal('fetchColumn', 0, $params);
-
- }
-
- /**
- * Executes the SQL statement and returns the first column of the result
- * @param array $params
- * @return array
- */
- public function queryColumn($params = array())
- {
- return $this->_queryInternal('fetchAll', PDO::FETCH_COLUMN, $params);
- }
-
- /**
- * Executes the SQL statement
- * @param string $method
- * @param mixed $mode
- * @param array $params
- * @return mixed
- */
- private function _queryInternal($method, $mode, $params = array())
- {
- $params = array_merge($this->_params, $params);
- return $this->_db->select($this->getText(), $params, $method, $mode);
- }
-
- /**
- * Builds a SQL SELECT statement from the given query specification
- * @param array $query
- * @return string the SQL statement
- */
- public function buildQuery($query)
- {
- $sql = !empty($query['distinct']) ? 'SELECT DISTINCT' : 'SELECT';
- $sql .= ' ' . (!empty($query['select']) ? $query['select'] : '*');
-
- $limit = isset($query['limit']) ? (int)$query['limit'] : '';
- $offset = isset($query['offset']) ? (int)$query['offset'] : '';
- $limits = $this->_applyLimit($limit, $offset);
-
- if (!empty($limits['before'])) {
- $sql .= "\n " . $limits['before'];
- }
- if (!empty($query['from'])) {
- $sql .= "\nFROM " . $query['from'];
- }
- if (!empty($query['join'])) {
- $sql .= "\n" . (is_array($query['join']) ? implode("\n", $query['join']) : $query['join']);
- }
- if (!empty($query['where'])) {
- $sql .= "\nWHERE " . $query['where'];
- }
- if (!empty($query['group'])) {
- $sql .= "\nGROUP BY " . $query['group'];
- }
- if (!empty($query['having'])) {
- $sql .= "\nHAVING " . $query['having'];
- }
- if (!empty($query['union'])) {
- $sql .= "\nUNION (\n" . (is_array($query['union']) ? implode("\n) UNION (\n", $query['union']) : $query['union']) . ')';
- }
- if (!empty($query['order'])) {
- $sql .= "\nORDER BY " . $query['order'];
- }
- if (!empty($limits['after'])) {
- $sql .= "\n " . $limits['after'];
- }
-
- return $sql;
- }
-
- /**
- * Sets SELECT part of the query
- * @param mixed $columns The columns to be selected (default '*' - all columns, or as array (e.g. array('id', 'name') )
- * @param string $option additional option that should be usaed, for example: SQL_CALC_FOUND_ROWS
- * @return CDbCommand command instance
- */
- public function select($columns = '*', $option = '')
- {
- if (is_string($columns) && strpos($columns, '(') !== false) {
- $this->_query['select'] = $columns;
- } else {
- if (!is_array($columns)) {
- $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
- }
-
- foreach ($columns as $key => $column) {
- if (is_object($column)) {
- $columns[$key] = (string)$column;
- } elseif (strpos($column, '(') === false) {
- // With alias
- if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/', $column, $matches)) {
- $columns[$key] = $this->_quotes($matches[1]) . ' AS ' . $this->_quotes($matches[2]);
- } else {
- $columns[$key] = $column !== '*' ? $this->_quotes($column) : '*';
- }
- }
- }
-
- $this->_query['select'] = implode(', ', $columns);
- }
-
- if ($option != '') {
- $this->_query['select'] = $option . ' ' . $this->_query['select'];
- }
-
- return $this;
- }
-
- /**
- * Returns the SELECT part of the query
- * @return string
- */
- public function getSelect()
- {
- return isset($this->_query['select']) ? $this->_query['select'] : '';
- }
-
- /**
- * Sets a SELECT part of the query with the DISTINCT flag turned ON
- * @param mixed $columns
- * @return CDbCommand command instance
- */
- public function selectDistinct($columns = '*')
- {
- $this->_query['distinct'] = true;
- return $this->select($columns);
- }
-
- /**
- * Sets a FROM part of the query
- * @param mixed string|array
- * @return CDbCommand command instance
- */
- public function from($tables)
- {
- if (is_string($tables) && strpos($tables, '(') !== false) {
- $this->_query['from'] = $tables;
- } else {
- if (!is_array($tables)) {
- $tables = preg_split('/\s*,\s*/', trim($tables), -1, PREG_SPLIT_NO_EMPTY);
- }
-
- foreach ($tables as $key => $table) {
- if (strpos($table, '(') === false) {
- // With alias
- if (preg_match('/^(.*?)(?i:\s+as|)\s+([^ ]+)$/', $table, $matches)) {
- $tables[$key] = $this->_quotes($matches[1]) . ' ' . $this->_quotes($matches[2]);
- } else {
- $tables[$key] = $this->_quotes($table);
- }
- }
- }
-
- $this->_query['from'] = implode(', ', $tables);
- }
-
- return $this;
- }
-
- /**
- * Returns a FROM part in the query
- * @return string
- */
- public function getFrom()
- {
- return isset($this->_query['from']) ? $this->_query['from'] : '';
- }
-
- /**
- * Sets the WHERE part of the query
- * @param mixed $conditions Ex.: array('and', 'id=1', 'id=2')
- * @param array $params
- * @return CDbCommand the command object itself
- */
- public function where($conditions, $params = array())
- {
- $this->_query['where'] = $this->_processConditions($conditions);
- foreach ($params as $name => $value) {
- $this->_params[$name] = $value;
- }
-
- return $this;
- }
-
- /**
- * Returns the WHERE part in the query
- * @return string
- */
- public function getWhere()
- {
- return isset($this->_query['where']) ? $this->_query['where'] : '';
- }
-
- /**
- * Sets the WHERE part of the query with AND
- * @param mixed $conditions Ex.: array('id=1', 'id=2')
- * @param array $params
- * @return CDbCommand the command object itself
- */
- public function andWhere($conditions, $params = array())
- {
- if (isset($this->_query['where'])) {
- $this->_query['where'] = $this->_processConditions(array('AND', $this->_query['where'], $conditions));
- } else {
- $this->_query['where'] = $this->_processConditions($conditions);
- }
-
- foreach ($params as $name => $value) {
- $this->_params[$name] = $value;
- }
-
- return $this;
- }
-
- /**
- * Sets the WHERE part of the query with OR
- * @param mixed $conditions Ex.: array('id=1', 'id=2')
- * @param array $params
- * @return CDbCommand the command object itself
- */
- public function orWhere($conditions, $params = array())
- {
- if (isset($this->_query['where'])) {
- $this->_query['where'] = $this->_processConditions(array('OR', $this->_query['where'], $conditions));
- } else {
- $this->_query['where'] = $this->_processConditions($conditions);
- }
-
- foreach ($params as $name => $value) {
- $this->_params[$name] = $value;
- }
-
- return $this;
- }
-
- /**
- * Appends an INNER JOIN part to the query
- * Ex.: join('table2', 'table1.id = table2.t_id')
- * @param string $table
- * @param mixed $conditions join condition that should appear in the ON part
- * @param array $params format: (name=>value) to be bound to the query
- * @return CDbCommand the command object itself
- */
- public function join($table, $conditions, $params = array())
- {
- return $this->_joinInternal('join', $table, $conditions, $params);
- }
-
- /**
- * Returns the join part in the query
- * @return mixed
- */
- public function getJoin()
- {
- return isset($this->_query['join']) ? $this->_query['join'] : '';
- }
-
- /**
- * Appends a LEFT OUTER JOIN part to the query
- * @param string $table
- * @param mixed $conditions join condition that should appear in the ON part
- * @param array $params format: (name=>value) to be bound to the query
- * @return CDbCommand the command object itself
- */
- public function leftJoin($table, $conditions, $params = array())
- {
- return $this->_joinInternal('left join', $table, $conditions, $params);
- }
-
- /**
- * Appends a RIGHT OUTER JOIN part to the query
- * @param string $table
- * @param mixed $conditions join condition that should appear in the ON part
- * @param array $params format: (name=>value) to be bound to the query
- * @return CDbCommand the command object itself
- */
- public function rightJoin($table, $conditions, $params = array())
- {
- return $this->_joinInternal('right join', $table, $conditions, $params);
- }
-
- /**
- * Appends a CROSS (INNER) JOIN part to the query
- * @param string $table
- * @return CDbCommand the command object itself
- */
- public function crossJoin($table)
- {
- return $this->_joinInternal('cross join', $table);
- }
-
- /**
- * Alias to crossJoin method
- */
- public function innerJoin($table)
- {
- return $this->_joinInternal('inner join', $table);
- }
-
- /**
- * Appends a NATURAL JOIN part to the query
- * @param string $table
- * @return CDbCommand the command object itself
- */
- public function naturalJoin($table)
- {
- return $this->_joinInternal('natural join', $table);
- }
-
- /**
- * Appends a NATURAL LEFT JOIN part to the query
- * @param string $table
- * @return CDbCommand the command object itself
- */
- public function naturalLeftJoin($table)
- {
- return $this->_joinInternal('natural left join', $table);
- }
-
- /**
- * Appends a NATURAL RIGHT JOIN part to the query
- * @param string $table
- * @return CDbCommand the command object itself
- */
- public function naturalRightJoin($table)
- {
- return $this->_joinInternal('natural right join', $table);
- }
-
- /**
- * Sets the GROUP BY part of the query
- * Ex.: columns specified in either a string (e.g. 'id', 'name') or an array (e.g. array('id', 'name'))
- * @return CDbCommand the command object itself
- */
- public function group($columns)
- {
- if (is_string($columns) && strpos($columns, '(') !== false) {
- $this->_query['group'] = $columns;
- } else {
- if (!is_array($columns)) {
- $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
- }
-
- foreach ($columns as $i => $column) {
- if (is_object($column)) {
- $columns[$i] = (string)$column;
- } elseif (strpos($column, '(') === false) {
- $columns[$i] = $this->_quotes($column);
- }
- }
-
- $this->_query['group'] = implode(', ', $columns);
- }
-
- return $this;
- }
-
- /**
- * Returns the GROUP BY part in the query
- * @return string (without 'GROUP BY')
- */
- public function getGroup()
- {
- return isset($this->_query['group']) ? $this->_query['group'] : '';
- }
-
- /**
- * Sets the HAVING part of the query
- * @param mixed $conditions
- * @param array $params
- * @return CDbCommand the command object itself
- */
- public function having($conditions, $params = array())
- {
- $this->_query['having'] = $this->_processConditions($conditions);
- foreach ($params as $name => $value) {
- $this->_params[$name] = $value;
- }
-
- return $this;
- }
-
- /**
- * Returns the HAVING part in the query
- * @return string (without 'HAVING')
- */
- public function getHaving()
- {
- return isset($this->_query['having']) ? $this->_query['having'] : '';
- }
-
- /**
- * Sets the ORDER BY part of the query.
- * @param mixed $columns (e.g. order(array('id ASC', 'name DESC')) or order('(1)'))
- * @return CDbCommand the command object itself
- */
- public function order($columns)
- {
- if (is_string($columns) && strpos($columns, '(') !== false) {
- $this->_query['order'] = $columns;
- } else {
- if (!is_array($columns)) {
- $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
- }
-
- foreach ($columns as $i => $column) {
- if (is_object($column)) {
- $columns[$i] = (string)$column;
- } elseif (strpos($column, '(') === false) {
- if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) {
- $columns[$i] = $this->_quotes($matches[1]) . ' ' . strtoupper($matches[2]);
- } else {
- $columns[$i] = $this->_quotes($column);
- }
- }
- }
-
- $this->_query['order'] = implode(', ', $columns);
- }
-
- return $this;
- }
-
- /**
- * Returns the ORDER BY part in the query
- * @return string (without 'ORDER BY')
- */
- public function getOrder()
- {
- return isset($this->_query['order']) ? $this->_query['order'] : '';
- }
-
- /**
- * Sets the LIMIT part of the query
- * @param int $limit
- * @param int $offset
- * @return CDbCommand the command object itself
- */
- public function limit($limit, $offset = null)
- {
- $this->_query['limit'] = (int)$limit;
- if ($offset !== null) {
- $this->offset($offset);
- }
-
- return $this;
- }
-
- /**
- * Returns the LIMIT part in the query
- * @return string (without 'LIMIT')
- */
- public function getLimit()
- {
- return isset($this->_query['limit']) ? $this->_query['limit'] : -1;
- }
-
- /**
- * Sets the OFFSET part of the query
- * @param int $offset
- * @return CDbCommand the command object itself
- */
- public function offset($offset)
- {
- $this->_query['offset'] = (int)$offset;
- return $this;
- }
-
- /**
- * Returns the OFFSET part in the query
- * @return string (without 'OFFSET')
- */
- public function getOffset()
- {
- return isset($this->_query['offset']) ? $this->_query['offset'] : -1;
- }
-
- /**
- * Appends a SQL statement using UNION operator
- * @param string $sql
- * @return CDbCommand the command object itself
- */
- public function union($sql)
- {
- if (isset($this->_query['union']) && is_string($this->_query['union'])) {
- $this->_query['union'] = array($this->_query['union']);
- }
-
- $this->_query['union'][] = $sql;
- return $this;
- }
-
- /**
- * Returns the UNION part in the query
- * @return mixed (without 'UNION')
- */
- public function getUnion()
- {
- return isset($this->_query['union']) ? $this->_query['union'] : '';
- }
-
- /**
- * Creates condition string that will be put in the WHERE part od the SQL statement
- * @param mixed $conditions
- * @return string
- */
- private function _processConditions($conditions)
- {
- if (empty($conditions)) {
- return '';
- } else if (!is_array($conditions)) {
- return $conditions;
- }
-
- $conditionsCount = count($conditions);
- $operator = strtoupper($conditions[0]);
- if (in_array($operator, array('OR', 'AND'))) {
- $parts = array();
- for ($i = 1; $i < $conditionsCount; $i++) {
- $condition = $this->_processConditions($conditions[$i]);
- if ($condition !== '') {
- $parts[] = '(' . $condition . ')';
- }
- }
- return !empty($parts) ? implode(' ' . $operator . ' ', $parts) : '';
- }
-
- if (!isset($conditions[1], $conditions[2])) {
- return '';
- }
-
- $column = $conditions[1];
- if (strpos($column, '(') === false) {
- $column = $this->_quotes($column);
- }
-
- $values = $conditions[2];
- if (!is_array($values)) {
- $values = array($values);
- }
-
- if (in_array($operator, array('IN', 'NOT IN'))) {
- if ($values === array()) {
- return $operator === 'IN' ? '0=1' : '';
- }
-
- foreach ($values as $i => $value) {
- $values[$i] = is_string($value) ? $this->_quotes($value) : (string)$value;
- }
-
- return $column . ' ' . $operator . ' (' . implode(', ', $values) . ')';
- }
-
- if (in_array($operator, array('LIKE', 'NOT LIKE', 'OR LIKE', 'OR NOT LIKE'))) {
- if (empty($values)) {
- return $operator === 'LIKE' || $operator === 'OR LIKE' ? '0=1' : '';
- }
-
- if ($operator === 'LIKE' || $operator === 'NOT LIKE') {
- $andor = ' AND ';
- } else {
- $andor = ' OR ';
- $operator = $operator === 'OR LIKE' ? 'LIKE' : 'NOT LIKE';
- }
-
- $expressions = array();
- foreach ($values as $value) {
- $expressions[] = $column . ' ' . $operator . ' ' . $this->_quotes($value);
- }
-
- return implode($andor, $expressions);
- }
-
- CDebug::addMessage('errors', 'wrong operator in condition ', A::t('core', 'Unknown operator "{operator}".', array('{operator}' => $operator)));
- }
-
- /**
- * Appends an JOIN part to the query
- * @param string $type Ex.:('join', 'left join', 'right join', 'cross join', 'natural join')
- * @param string $table
- * @param mixed $conditions
- * @param array $params
- * @return CDbCommand the command object itself
- */
- private function _joinInternal($type, $table, $conditions = '', $params = array())
- {
- if (strpos($table, '(') === false) {
- if (preg_match('/^(.*?)(?i:\s+as|)\s+([^ ]+)$/', $table, $matches)) {
- // With alias
- $table = $this->_connection->quoteTableName($matches[1]) . ' ' . $this->_connection->quoteTableName($matches[2]);
- } else {
- $table = $this->_connection->quoteTableName($table);
- }
- }
-
- $conditions = $this->_processConditions($conditions);
- if ($conditions != '') {
- $conditions = ' ON ' . $conditions;
- }
-
- if (isset($this->_query['join']) && is_string($this->_query['join'])) {
- $this->_query['join'] = array($this->_query['join']);
- }
-
- $this->_query['join'][] = strtoupper($type) . ' ' . $table . $conditions;
- foreach ($params as $name => $value) {
- $this->_params[$name] = $value;
- }
-
- return $this;
- }
-
- /**
- * Escapes given string with backquotes
- * Prepares table name for using in SQL statements
- * @param string $string
- * @return string
- */
- private function _quotes($string = '')
- {
- return $this->_backQuote . $string . $this->_backQuote;
- }
-
- /**
- * Prepare LIMIT clause for SQL statement
- * @param string $limit
- * @retun array
- */
- private function _applyLimit($limit, $offset = '')
- {
- $limits = array('before' => '', 'after' => '');
-
- if (!empty($limit)) {
- if (preg_match('/mssql|sqlsrv/i', $this->_dbDriver)) {
- $limits['before'] = !empty($limit) ? ' TOP ' . $limit : '';
- } else {
- $limits['after'] = !empty($limit) ? ' LIMIT ' . (!empty($offset) ? ', ' : '') . ' ' . $limit : '';
- }
- }
-
- return $limits;
- }
+ /** @var CDatabase */
+ protected $_db;
+ /** @var */
+ protected $_dbDriver = '';
+ /** @var array */
+ public $_params = [];
+
+ /** @var string */
+ private $_text;
+ /** @var string */
+ private $_statement;
+ /** @var string */
+ private $_query;
+ /** @var char */
+ private $_backQuote = '`';
+ /** @var int */
+ private $_fetchMode = PDO::FETCH_ASSOC;
+
+
+ /**
+ * Class constructor
+ *
+ * @param CDatabase $dbConnection
+ */
+ public function __construct($dbConnection = null)
+ {
+ $this->_db = $dbConnection;
+ $this->_dbDriver = CConfig::get('db.driver');
+
+ // Set back quote according to database driver
+ if (preg_match('/mssql|sqlsrv/i', CConfig::get('db.driver'))) {
+ $this->_backQuote = '';
+ }
+ }
+
+ /**
+ * Cleans up the command for building a new query
+ *
+ * @return CDbCommand command instance
+ */
+ public function reset()
+ {
+ $this->_text = null;
+ $this->_query = null;
+ $this->_statement = null;
+ $this->_params = [];
+
+ return $this;
+ }
+
+ /**
+ * Cancels execution of the SQL statement
+ */
+ public function cancel()
+ {
+ $this->_statement = null;
+ }
+
+ /**
+ * Defines SQL statement to be executed
+ *
+ * @return CDbCommand command instance
+ */
+ public function setText($value)
+ {
+ $this->_text = $value;
+ $this->cancel();
+
+ return $this;
+ }
+
+ /**
+ * Returns SQL to be executed
+ *
+ * @return string
+ */
+ public function getText()
+ {
+ if ($this->_text == '' && ! empty($this->_query)) {
+ $this->setText($this->buildQuery($this->_query));
+ }
+
+ return $this->_text;
+ }
+
+ /**
+ * Executes the SQL statement and returns all rows
+ *
+ * @param boolean $fetchAssociative
+ * @param array $params
+ *
+ * @return array
+ */
+ public function queryAll($fetchAssociative = true, $params = [])
+ {
+ return $this->_queryInternal('fetchAll', $fetchAssociative ? $this->_fetchMode : PDO::FETCH_NUM, $params);
+ }
+
+ /**
+ * Executes the SQL statement and returns the first row of the result.
+ *
+ * @param boolean $fetchAssociative
+ * @param array $params
+ *
+ * @return array
+ */
+ public function queryRow($fetchAssociative = true, $params = [])
+ {
+ return $this->_queryInternal('fetch', $fetchAssociative ? $this->_fetchMode : PDO::FETCH_NUM, $params);
+ }
+
+ /**
+ * Executes the SQL statement and returns the value of the first column in the first row of data
+ *
+ * @param array $params
+ *
+ * @return array
+ */
+ public function queryScalar($params = [])
+ {
+ return $this->_queryInternal('fetchColumn', 0, $params);
+ }
+
+ /**
+ * Executes the SQL statement and returns the first column of the result
+ *
+ * @param array $params
+ *
+ * @return array
+ */
+ public function queryColumn($params = [])
+ {
+ return $this->_queryInternal('fetchAll', PDO::FETCH_COLUMN, $params);
+ }
+
+ /**
+ * Executes the SQL statement
+ *
+ * @param string $method
+ * @param mixed $mode
+ * @param array $params
+ *
+ * @return mixed
+ */
+ private function _queryInternal($method, $mode, $params = [])
+ {
+ $params = array_merge($this->_params, $params);
+
+ return $this->_db->select($this->getText(), $params, $method, $mode);
+ }
+
+ /**
+ * Builds a SQL SELECT statement from the given query specification
+ *
+ * @param array $query
+ *
+ * @return string the SQL statement
+ */
+ public function buildQuery($query)
+ {
+ $sql = ! empty($query['distinct']) ? 'SELECT DISTINCT' : 'SELECT';
+ $sql .= ' '.(! empty($query['select']) ? $query['select'] : '*');
+
+ $limit = isset($query['limit']) ? (int)$query['limit'] : '';
+ $offset = isset($query['offset']) ? (int)$query['offset'] : '';
+ $limits = $this->_applyLimit($limit, $offset);
+
+ if ( ! empty($limits['before'])) {
+ $sql .= "\n ".$limits['before'];
+ }
+ if ( ! empty($query['from'])) {
+ $sql .= "\nFROM ".$query['from'];
+ }
+ if ( ! empty($query['join'])) {
+ $sql .= "\n".(is_array($query['join']) ? implode("\n", $query['join']) : $query['join']);
+ }
+ if ( ! empty($query['where'])) {
+ $sql .= "\nWHERE ".$query['where'];
+ }
+ if ( ! empty($query['group'])) {
+ $sql .= "\nGROUP BY ".$query['group'];
+ }
+ if ( ! empty($query['having'])) {
+ $sql .= "\nHAVING ".$query['having'];
+ }
+ if ( ! empty($query['union'])) {
+ $sql .= "\nUNION (\n".(is_array($query['union']) ? implode("\n) UNION (\n", $query['union'])
+ : $query['union']).')';
+ }
+ if ( ! empty($query['order'])) {
+ $sql .= "\nORDER BY ".$query['order'];
+ }
+ if ( ! empty($limits['after'])) {
+ $sql .= "\n ".$limits['after'];
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Sets SELECT part of the query
+ *
+ * @param mixed $columns The columns to be selected (default '*' - all columns, or as array (e.g. ['id', 'name'] )
+ * @param string $option additional option that should be usaed, for example: SQL_CALC_FOUND_ROWS
+ *
+ * @return CDbCommand command instance
+ */
+ public function select($columns = '*', $option = '')
+ {
+ if (is_string($columns) && strpos($columns, '(') !== false) {
+ $this->_query['select'] = $columns;
+ } else {
+ if ( ! is_array($columns)) {
+ $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
+ }
+
+ foreach ($columns as $key => $column) {
+ if (is_object($column)) {
+ $columns[$key] = (string)$column;
+ } elseif (strpos($column, '(') === false) {
+ // With alias
+ if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/', $column, $matches)) {
+ $columns[$key] = $this->_quotes($matches[1]).' AS '.$this->_quotes($matches[2]);
+ } else {
+ $columns[$key] = $column !== '*' ? $this->_quotes($column) : '*';
+ }
+ }
+ }
+
+ $this->_query['select'] = implode(', ', $columns);
+ }
+
+ if ($option != '') {
+ $this->_query['select'] = $option.' '.$this->_query['select'];
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns the SELECT part of the query
+ *
+ * @return string
+ */
+ public function getSelect()
+ {
+ return isset($this->_query['select']) ? $this->_query['select'] : '';
+ }
+
+ /**
+ * Sets a SELECT part of the query with the DISTINCT flag turned ON
+ *
+ * @param mixed $columns
+ *
+ * @return CDbCommand command instance
+ */
+ public function selectDistinct($columns = '*')
+ {
+ $this->_query['distinct'] = true;
+
+ return $this->select($columns);
+ }
+
+ /**
+ * Sets a FROM part of the query
+ *
+ * @param mixed string|array
+ *
+ * @return CDbCommand command instance
+ */
+ public function from($tables)
+ {
+ if (is_string($tables) && strpos($tables, '(') !== false) {
+ $this->_query['from'] = $tables;
+ } else {
+ if ( ! is_array($tables)) {
+ $tables = preg_split('/\s*,\s*/', trim($tables), -1, PREG_SPLIT_NO_EMPTY);
+ }
+
+ foreach ($tables as $key => $table) {
+ if (strpos($table, '(') === false) {
+ // With alias
+ if (preg_match('/^(.*?)(?i:\s+as|)\s+([^ ]+)$/', $table, $matches)) {
+ $tables[$key] = $this->_quotes($matches[1]).' '.$this->_quotes($matches[2]);
+ } else {
+ $tables[$key] = $this->_quotes($table);
+ }
+ }
+ }
+
+ $this->_query['from'] = implode(', ', $tables);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns a FROM part in the query
+ *
+ * @return string
+ */
+ public function getFrom()
+ {
+ return isset($this->_query['from']) ? $this->_query['from'] : '';
+ }
+
+ /**
+ * Sets the WHERE part of the query
+ *
+ * @param mixed $conditions Ex.: ['and', 'id=1', 'id=2']
+ * @param array $params
+ *
+ * @return CDbCommand the command object itself
+ */
+ public function where($conditions, $params = [])
+ {
+ $this->_query['where'] = $this->_processConditions($conditions);
+ foreach ($params as $name => $value) {
+ $this->_params[$name] = $value;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns the WHERE part in the query
+ *
+ * @return string
+ */
+ public function getWhere()
+ {
+ return isset($this->_query['where']) ? $this->_query['where'] : '';
+ }
+
+ /**
+ * Sets the WHERE part of the query with AND
+ *
+ * @param mixed $conditions Ex.: ['id=1', 'id=2']
+ * @param array $params
+ *
+ * @return CDbCommand the command object itself
+ */
+ public function andWhere($conditions, $params = [])
+ {
+ if (isset($this->_query['where'])) {
+ $this->_query['where'] = $this->_processConditions(['AND', $this->_query['where'], $conditions]);
+ } else {
+ $this->_query['where'] = $this->_processConditions($conditions);
+ }
+
+ foreach ($params as $name => $value) {
+ $this->_params[$name] = $value;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Sets the WHERE part of the query with OR
+ *
+ * @param mixed $conditions Ex.: array('id=1', 'id=2')
+ * @param array $params
+ *
+ * @return CDbCommand the command object itself
+ */
+ public function orWhere($conditions, $params = [])
+ {
+ if (isset($this->_query['where'])) {
+ $this->_query['where'] = $this->_processConditions(['OR', $this->_query['where'], $conditions]);
+ } else {
+ $this->_query['where'] = $this->_processConditions($conditions);
+ }
+
+ foreach ($params as $name => $value) {
+ $this->_params[$name] = $value;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Appends an INNER JOIN part to the query
+ * Ex.: join('table2', 'table1.id = table2.t_id')
+ *
+ * @param string $table
+ * @param mixed $conditions join condition that should appear in the ON part
+ * @param array $params format: (name=>value) to be bound to the query
+ *
+ * @return CDbCommand the command object itself
+ */
+ public function join($table, $conditions, $params = [])
+ {
+ return $this->_joinInternal('join', $table, $conditions, $params);
+ }
+
+ /**
+ * Returns the join part in the query
+ *
+ * @return mixed
+ */
+ public function getJoin()
+ {
+ return isset($this->_query['join']) ? $this->_query['join'] : '';
+ }
+
+ /**
+ * Appends a LEFT OUTER JOIN part to the query
+ *
+ * @param string $table
+ * @param mixed $conditions join condition that should appear in the ON part
+ * @param array $params format: (name=>value) to be bound to the query
+ *
+ * @return CDbCommand the command object itself
+ */
+ public function leftJoin($table, $conditions, $params = [])
+ {
+ return $this->_joinInternal('left join', $table, $conditions, $params);
+ }
+
+ /**
+ * Appends a RIGHT OUTER JOIN part to the query
+ *
+ * @param string $table
+ * @param mixed $conditions join condition that should appear in the ON part
+ * @param array $params format: (name=>value) to be bound to the query
+ *
+ * @return CDbCommand the command object itself
+ */
+ public function rightJoin($table, $conditions, $params = [])
+ {
+ return $this->_joinInternal('right join', $table, $conditions, $params);
+ }
+
+ /**
+ * Appends a CROSS (INNER) JOIN part to the query
+ *
+ * @param string $table
+ *
+ * @return CDbCommand the command object itself
+ */
+ public function crossJoin($table)
+ {
+ return $this->_joinInternal('cross join', $table);
+ }
+
+ /**
+ * Alias to crossJoin method
+ */
+ public function innerJoin($table)
+ {
+ return $this->_joinInternal('inner join', $table);
+ }
+
+ /**
+ * Appends a NATURAL JOIN part to the query
+ *
+ * @param string $table
+ *
+ * @return CDbCommand the command object itself
+ */
+ public function naturalJoin($table)
+ {
+ return $this->_joinInternal('natural join', $table);
+ }
+
+ /**
+ * Appends a NATURAL LEFT JOIN part to the query
+ *
+ * @param string $table
+ *
+ * @return CDbCommand the command object itself
+ */
+ public function naturalLeftJoin($table)
+ {
+ return $this->_joinInternal('natural left join', $table);
+ }
+
+ /**
+ * Appends a NATURAL RIGHT JOIN part to the query
+ *
+ * @param string $table
+ *
+ * @return CDbCommand the command object itself
+ */
+ public function naturalRightJoin($table)
+ {
+ return $this->_joinInternal('natural right join', $table);
+ }
+
+ /**
+ * Sets the GROUP BY part of the query
+ * Ex.: columns specified in either a string (e.g. 'id', 'name') or an array (e.g. array('id', 'name'))
+ *
+ * @return CDbCommand the command object itself
+ */
+ public function group($columns)
+ {
+ if (is_string($columns) && strpos($columns, '(') !== false) {
+ $this->_query['group'] = $columns;
+ } else {
+ if ( ! is_array($columns)) {
+ $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
+ }
+
+ foreach ($columns as $i => $column) {
+ if (is_object($column)) {
+ $columns[$i] = (string)$column;
+ } elseif (strpos($column, '(') === false) {
+ $columns[$i] = $this->_quotes($column);
+ }
+ }
+
+ $this->_query['group'] = implode(', ', $columns);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns the GROUP BY part in the query
+ *
+ * @return string (without 'GROUP BY')
+ */
+ public function getGroup()
+ {
+ return isset($this->_query['group']) ? $this->_query['group'] : '';
+ }
+
+ /**
+ * Sets the HAVING part of the query
+ *
+ * @param mixed $conditions
+ * @param array $params
+ *
+ * @return CDbCommand the command object itself
+ */
+ public function having($conditions, $params = [])
+ {
+ $this->_query['having'] = $this->_processConditions($conditions);
+ foreach ($params as $name => $value) {
+ $this->_params[$name] = $value;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns the HAVING part in the query
+ *
+ * @return string (without 'HAVING')
+ */
+ public function getHaving()
+ {
+ return isset($this->_query['having']) ? $this->_query['having'] : '';
+ }
+
+ /**
+ * Sets the ORDER BY part of the query.
+ *
+ * @param mixed $columns (e.g. order(array('id ASC', 'name DESC')) or order('(1)'))
+ *
+ * @return CDbCommand the command object itself
+ */
+ public function order($columns)
+ {
+ if (is_string($columns) && strpos($columns, '(') !== false) {
+ $this->_query['order'] = $columns;
+ } else {
+ if ( ! is_array($columns)) {
+ $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
+ }
+
+ foreach ($columns as $i => $column) {
+ if (is_object($column)) {
+ $columns[$i] = (string)$column;
+ } elseif (strpos($column, '(') === false) {
+ if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) {
+ $columns[$i] = $this->_quotes($matches[1]).' '.strtoupper($matches[2]);
+ } else {
+ $columns[$i] = $this->_quotes($column);
+ }
+ }
+ }
+
+ $this->_query['order'] = implode(', ', $columns);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns the ORDER BY part in the query
+ *
+ * @return string (without 'ORDER BY')
+ */
+ public function getOrder()
+ {
+ return isset($this->_query['order']) ? $this->_query['order'] : '';
+ }
+
+ /**
+ * Sets the LIMIT part of the query
+ *
+ * @param int $limit
+ * @param int $offset
+ *
+ * @return CDbCommand the command object itself
+ */
+ public function limit($limit, $offset = null)
+ {
+ $this->_query['limit'] = (int)$limit;
+ if ($offset !== null) {
+ $this->offset($offset);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns the LIMIT part in the query
+ *
+ * @return string (without 'LIMIT')
+ */
+ public function getLimit()
+ {
+ return isset($this->_query['limit']) ? $this->_query['limit'] : -1;
+ }
+
+ /**
+ * Sets the OFFSET part of the query
+ *
+ * @param int $offset
+ *
+ * @return CDbCommand the command object itself
+ */
+ public function offset($offset)
+ {
+ $this->_query['offset'] = (int)$offset;
+
+ return $this;
+ }
+
+ /**
+ * Returns the OFFSET part in the query
+ *
+ * @return string (without 'OFFSET')
+ */
+ public function getOffset()
+ {
+ return isset($this->_query['offset']) ? $this->_query['offset'] : -1;
+ }
+
+ /**
+ * Appends a SQL statement using UNION operator
+ *
+ * @param string $sql
+ *
+ * @return CDbCommand the command object itself
+ */
+ public function union($sql)
+ {
+ if (isset($this->_query['union']) && is_string($this->_query['union'])) {
+ $this->_query['union'] = [$this->_query['union']];
+ }
+
+ $this->_query['union'][] = $sql;
+
+ return $this;
+ }
+
+ /**
+ * Returns the UNION part in the query
+ *
+ * @return mixed (without 'UNION')
+ */
+ public function getUnion()
+ {
+ return isset($this->_query['union']) ? $this->_query['union'] : '';
+ }
+
+ /**
+ * Creates condition string that will be put in the WHERE part od the SQL statement
+ *
+ * @param mixed $conditions
+ *
+ * @return string
+ */
+ private function _processConditions($conditions)
+ {
+ if (empty($conditions)) {
+ return '';
+ } elseif ( ! is_array($conditions)) {
+ return $conditions;
+ }
+
+ $conditionsCount = count($conditions);
+ $operator = strtoupper($conditions[0]);
+ if (in_array($operator, ['OR', 'AND'])) {
+ $parts = [];
+ for ($i = 1; $i < $conditionsCount; $i++) {
+ $condition = $this->_processConditions($conditions[$i]);
+ if ($condition !== '') {
+ $parts[] = '('.$condition.')';
+ }
+ }
+
+ return ! empty($parts) ? implode(' '.$operator.' ', $parts) : '';
+ }
+
+ if ( ! isset($conditions[1], $conditions[2])) {
+ return '';
+ }
+
+ $column = $conditions[1];
+ if (strpos($column, '(') === false) {
+ $column = $this->_quotes($column);
+ }
+
+ $values = $conditions[2];
+ if ( ! is_array($values)) {
+ $values = [$values];
+ }
+
+ if (in_array($operator, ['IN', 'NOT IN'])) {
+ if ($values === []) {
+ return $operator === 'IN' ? '0=1' : '';
+ }
+
+ foreach ($values as $i => $value) {
+ $values[$i] = is_string($value) ? $this->_quotes($value) : (string)$value;
+ }
+
+ return $column.' '.$operator.' ('.implode(', ', $values).')';
+ }
+
+ if (in_array($operator, ['LIKE', 'NOT LIKE', 'OR LIKE', 'OR NOT LIKE'])) {
+ if (empty($values)) {
+ return $operator === 'LIKE' || $operator === 'OR LIKE' ? '0=1' : '';
+ }
+
+ if ($operator === 'LIKE' || $operator === 'NOT LIKE') {
+ $andor = ' AND ';
+ } else {
+ $andor = ' OR ';
+ $operator = $operator === 'OR LIKE' ? 'LIKE' : 'NOT LIKE';
+ }
+
+ $expressions = [];
+ foreach ($values as $value) {
+ $expressions[] = $column.' '.$operator.' '.$this->_quotes($value);
+ }
+
+ return implode($andor, $expressions);
+ }
+
+ CDebug::addMessage(
+ 'errors',
+ 'wrong operator in condition ',
+ A::t('core', 'Unknown operator "{operator}".', ['{operator}' => $operator])
+ );
+ }
+
+ /**
+ * Appends an JOIN part to the query
+ *
+ * @param string $type Ex.:('join', 'left join', 'right join', 'cross join', 'natural join')
+ * @param string $table
+ * @param mixed $conditions
+ * @param array $params
+ *
+ * @return CDbCommand the command object itself
+ */
+ private function _joinInternal($type, $table, $conditions = '', $params = [])
+ {
+ if (strpos($table, '(') === false) {
+ if (preg_match('/^(.*?)(?i:\s+as|)\s+([^ ]+)$/', $table, $matches)) {
+ // With alias
+ $table = $this->_connection->quoteTableName($matches[1]).' '.$this->_connection->quoteTableName(
+ $matches[2]
+ );
+ } else {
+ $table = $this->_connection->quoteTableName($table);
+ }
+ }
+
+ $conditions = $this->_processConditions($conditions);
+ if ($conditions != '') {
+ $conditions = ' ON '.$conditions;
+ }
+
+ if (isset($this->_query['join']) && is_string($this->_query['join'])) {
+ $this->_query['join'] = [$this->_query['join']];
+ }
+
+ $this->_query['join'][] = strtoupper($type).' '.$table.$conditions;
+ foreach ($params as $name => $value) {
+ $this->_params[$name] = $value;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Escapes given string with backquotes
+ * Prepares table name for using in SQL statements
+ *
+ * @param string $string
+ *
+ * @return string
+ */
+ private function _quotes($string = '')
+ {
+ return $this->_backQuote.$string.$this->_backQuote;
+ }
+
+ /**
+ * Prepare LIMIT clause for SQL statement
+ *
+ * @param string $limit
+ * @param string $offset
+ *
+ * @return array
+ * @retun array
+ */
+ private function _applyLimit($limit, $offset = '')
+ {
+ $limits = ['before' => '', 'after' => ''];
+
+ if ( ! empty($limit)) {
+ if (preg_match('/mssql|sqlsrv/i', $this->_dbDriver)) {
+ $limits['before'] = ! empty($limit) ? ' TOP '.$limit : '';
+ } else {
+ $limits['after'] = ! empty($limit) ? ' LIMIT '.(! empty($offset) ? ', ' : '').' '.$limit : '';
+ }
+ }
+
+ return $limits;
+ }
}
diff --git a/framework/db/CRecordEntity.php b/framework/db/CRecordEntity.php
index 6d58427..04ccb4f 100644
--- a/framework/db/CRecordEntity.php
+++ b/framework/db/CRecordEntity.php
@@ -1,7 +1,7 @@
@@ -15,6 +15,7 @@
* __construct
* __set
* __get
+ * __isset
* __unset
* set
* get
@@ -31,199 +32,252 @@
abstract class CRecordEntity
{
- /** @var */
- protected $_columns = array();
- /** @var */
- protected $_primaryKey = '';
- /** @var */
- protected $_pkValue = 0;
-
- /** @var fillable fields */
- protected $_fillable = array();
- /** @var guarded fields */
- protected $_guarded = array();
-
- /**
- * Class constructor
- * @param int $pkVal
- */
- public function __construct($pkVal = 0)
- {
- if (!empty($pkVal)) {
- $this->_pkValue = $pkVal;
- }
- }
-
- /**
- * Setter
- * @param string $index
- * @param mixed $value
- * @return void
- */
- public function __set($index, $value)
- {
- $this->_columns[$index] = $value;
- }
-
- /**
- * Getter
- * @param string $index
- * @return string
- */
- public function __get($index)
- {
- if (array_key_exists($index, $this->_columns)) {
- return $this->_columns[$index];
- } else {
- CDebug::AddMessage('errors', 'wrong_column' . $index, A::t('core', 'Wrong column name: {index} in table {table}', array('{index}' => $index, '{table}' => __CLASS__)));
- return '';
- }
- }
-
- /**
- * Sets a active record property to be null
- * @param string $index
- * @return void
- */
- public function __unset($index)
- {
- if (array_key_exists($index, $this->_columns)) {
- unset($this->_columns[$index]);
- }
- }
-
- /**
- * Setter
- * @param string $index
- * @param mixed $value
- * @return void
- */
- public function set($index, $value)
- {
- $this->_columns[$index] = $value;
- }
-
- /**
- * Getter
- * @param string $index
- * @return string
- */
- public function get($index)
- {
- if (array_key_exists($index, $this->_columns)) {
- return $this->_columns[$index];
- } else {
- CDebug::AddMessage('errors', 'wrong_column' . $index, A::t('core', 'Wrong column name: {index} in table {table}', array('{index}' => $index, '{table}' => $this->_table)));
- return '';
- }
- }
-
- /**
- * Returns the primary key of the associated database table
- * @return string
- */
- public function primaryKey()
- {
- return $this->_primaryKey;
- }
-
- /**
- * Returns the primary key value
- * @return mixed
- */
- public function getPrimaryKey()
- {
- return $this->_pkValue;
- }
-
- /**
- * Returns the primary key value
- * @param int $pkVal
- */
- protected function setPrimaryKey($pkVal = 0)
- {
- if (!empty($pkVal)) {
- $this->_pkValue = $pkVal;
- }
- }
-
- /**
- * Return all columns
- * @param bool $allowFilters Return only allowed fields
- * @return array
- */
- public function columns($allowFilters = false)
- {
- $columns = $this->_columns;
-
- if ($allowFilters) {
- // Validate fillable fields, left only allowed fields
- if (is_array($this->_fillable) && !empty($this->_fillable)) {
- $columns = array_intersect_key($columns, array_flip($this->_fillable));
- }
-
- // Validate guarded fields, exclude guarded fields
- if (is_array($this->_guarded) && !empty($this->_guarded)) {
- $columns = array_diff_key($columns, array_flip($this->_guarded));
- }
- }
-
- return $columns;
- }
-
- /**
- * Return all allowed columns
- * @return array
- */
- public function allowedColumns()
- {
- return $this->columns(true);
- }
-
- /**
- * Set fillable fields
- * @param array $fields
- * @return void
- */
- public function setFillable($fields = array())
- {
- if (is_array($fields)) {
- $this->_fillable = array();
- foreach ($fields as $field) {
- $this->_fillable[] = $field;
- }
- }
- }
-
- /**
- * Set guarded fields
- * @param array $fields
- * @return void
- */
- public function setGuarded($fields = array())
- {
- if (is_array($fields)) {
- $this->_guarded= array();
- foreach ($fields as $field) {
- $this->_guarded[] = $field;
- }
- }
- }
-
- /**
- * Fills data from array
- * @param array|null $record
- * @return bool
- */
- public function fillFromArray($record = null)
- {
- // Copy data to CRecordEntity
- if (!is_null($record) && is_array($record)) {
- foreach ($record as $key => $val) {
- $this->$key = $val;
- }
- }
-
- return true;
- }
+ /** @var */
+ protected $_columns = [];
+ /** @var */
+ protected $_primaryKey = '';
+ /** @var */
+ protected $_pkValue = 0;
+
+ /** @var fillable fields */
+ protected $_fillable = [];
+ /** @var guarded fields */
+ protected $_guarded = [];
+
+ /**
+ * Class constructor
+ *
+ * @param int $pkVal
+ */
+ public function __construct($pkVal = 0)
+ {
+ if ( ! empty($pkVal)) {
+ $this->_pkValue = $pkVal;
+ }
+ }
+
+ /**
+ * Setter
+ *
+ * @param string $index
+ * @param mixed $value
+ *
+ * @return void
+ */
+ public function __set($index, $value)
+ {
+ $this->_columns[$index] = $value;
+ }
+
+ /**
+ * Getter
+ *
+ * @param string $index
+ *
+ * @return string
+ */
+ public function __get($index)
+ {
+ if (array_key_exists($index, $this->_columns)) {
+ return $this->_columns[$index];
+ } else {
+ CDebug::AddMessage(
+ 'errors',
+ 'wrong_column'.$index,
+ A::t(
+ 'core',
+ 'Wrong column name: {index} in table {table}',
+ ['{index}' => $index, '{table}' => __CLASS__]
+ )
+ );
+
+ return '';
+ }
+ }
+
+ /**
+ * Checks if record entity property exists
+ *
+ * @param string $index
+ *
+ * @return bool
+ */
+ public function __isset($index)
+ {
+ return array_key_exists($index, $this->_columns) ? true : false;
+ }
+
+ /**
+ * Sets a record entity property to be null
+ *
+ * @param string $index
+ *
+ * @return void
+ */
+ public function __unset($index)
+ {
+ if (array_key_exists($index, $this->_columns)) {
+ unset($this->_columns[$index]);
+ }
+ }
+
+ /**
+ * Setter
+ *
+ * @param string $index
+ * @param mixed $value
+ *
+ * @return void
+ */
+ public function set($index, $value)
+ {
+ $this->_columns[$index] = $value;
+ }
+
+ /**
+ * Getter
+ *
+ * @param string $index
+ *
+ * @return string
+ */
+ public function get($index)
+ {
+ if (array_key_exists($index, $this->_columns)) {
+ return $this->_columns[$index];
+ } else {
+ CDebug::AddMessage(
+ 'errors',
+ 'wrong_column'.$index,
+ A::t(
+ 'core',
+ 'Wrong column name: {index} in table {table}',
+ ['{index}' => $index, '{table}' => $this->_table]
+ )
+ );
+
+ return '';
+ }
+ }
+
+ /**
+ * Returns the primary key of the associated database table
+ *
+ * @return string
+ */
+ public function primaryKey()
+ {
+ return $this->_primaryKey;
+ }
+
+ /**
+ * Returns the primary key value
+ *
+ * @return mixed
+ */
+ public function getPrimaryKey()
+ {
+ return $this->_pkValue;
+ }
+
+ /**
+ * Returns the primary key value
+ *
+ * @param int $pkVal
+ */
+ protected function setPrimaryKey($pkVal = 0)
+ {
+ if ( ! empty($pkVal)) {
+ $this->_pkValue = $pkVal;
+ }
+ }
+
+ /**
+ * Return all columns
+ *
+ * @param bool $allowFilters Return only allowed fields
+ *
+ * @return array
+ */
+ public function columns($allowFilters = false)
+ {
+ $columns = $this->_columns;
+
+ if ($allowFilters) {
+ // Validate fillable fields, left only allowed fields
+ if (is_array($this->_fillable) && ! empty($this->_fillable)) {
+ $columns = array_intersect_key($columns, array_flip($this->_fillable));
+ }
+
+ // Validate guarded fields, exclude guarded fields
+ if (is_array($this->_guarded) && ! empty($this->_guarded)) {
+ $columns = array_diff_key($columns, array_flip($this->_guarded));
+ }
+ }
+
+ return $columns;
+ }
+
+ /**
+ * Return all allowed columns
+ *
+ * @return array
+ */
+ public function allowedColumns()
+ {
+ return $this->columns(true);
+ }
+
+ /**
+ * Set fillable fields
+ *
+ * @param array $fields
+ *
+ * @return void
+ */
+ public function setFillable($fields = [])
+ {
+ if (is_array($fields)) {
+ $this->_fillable = [];
+ foreach ($fields as $field) {
+ $this->_fillable[] = $field;
+ }
+ }
+ }
+
+ /**
+ * Set guarded fields
+ *
+ * @param array $fields
+ *
+ * @return void
+ */
+ public function setGuarded($fields = [])
+ {
+ if (is_array($fields)) {
+ $this->_guarded = [];
+ foreach ($fields as $field) {
+ $this->_guarded[] = $field;
+ }
+ }
+ }
+
+ /**
+ * Fills data from array
+ *
+ * @param array|null $record
+ *
+ * @return bool
+ */
+ public function fillFromArray($record = null)
+ {
+ // Copy data to CRecordEntity
+ if ( ! is_null($record) && is_array($record)) {
+ foreach ($record as $key => $val) {
+ $this->$key = $val;
+ }
+ }
+
+ return true;
+ }
}
\ No newline at end of file
diff --git a/framework/helpers/CArray.php b/framework/helpers/CArray.php
index 008f9cf..efeec9b 100644
--- a/framework/helpers/CArray.php
+++ b/framework/helpers/CArray.php
@@ -24,30 +24,30 @@ class CArray
* Exchanges all keys with values from defined field in sub-arrays
*
* Usage:
- * $array = array(
- * [0] => array(
+ * $array = [
+ * [0] => [
* 'field1' => value01
* 'field2' => value02
* 'field3' => value03
- * ),
- * [1] => array(
+ * ],
+ * [1] => [
* 'field1' => value11
* 'field2' => value12
* 'field3' => value13
- * ));
+ * ]];
*
* Result:
- * $array = array(
- * ['value01'] => array(
+ * $array = [
+ * ['value01'] => [
* 'field1' => value01
* 'field2' => value02
* 'field3' => value03
- * ),
- * ['value11'] => array(
+ * ],
+ * ['value11'] => [
* 'field1' => value11
* 'field2' => value12
* 'field3' => value13
- * ));
+ * ]];
*
* flipByField($array, 'field3');
*
@@ -79,17 +79,17 @@ public static function flipByField($array, $field = '', $group = false)
* Returns array of unique values from specified filed in sub-array
*
* Usage:
- * $array = array(
- * [0] => array(
+ * $array = [
+ * [0] => [
* 'field1' => ..v1
* 'field2' => ..v2
* 'field3' => ..v3
- * ),
- * [1] => array(
+ * ],
+ * [1] => [
* 'field1' => ..v1
* 'field2' => ..v2
* 'field3' => ..v3
- * ));
+ * ]];
*
* uniqueByField($array, 'field3');
*
@@ -100,9 +100,9 @@ public static function flipByField($array, $field = '', $group = false)
*/
public static function uniqueByField($array, $field = '', $unique = false)
{
- $return = array();
-
- if (is_array($array)) {
+ $return = [];
+
+ if (is_array($array)) {
foreach ($array as $k => $v) {
if (isset($v[$field])) {
$return[] = $v[$field];
@@ -126,9 +126,9 @@ public static function uniqueByField($array, $field = '', $unique = false)
public static function changeKeysCase($array, $case = CASE_LOWER)
{
$function = ($case == CASE_UPPER) ? 'strtoupper' : 'strtolower';
- $newArray = array();
-
- foreach ($array as $key => $value) {
+ $newArray = [];
+
+ foreach ($array as $key => $value) {
if (is_array($value)) {
// $value is an array, handle keys too
$newArray[$function($key)] = self::changeKeysCase($value, $case);
diff --git a/framework/helpers/CConvert.php b/framework/helpers/CConvert.php
index 7ed3ba9..ac02dd8 100644
--- a/framework/helpers/CConvert.php
+++ b/framework/helpers/CConvert.php
@@ -8,34 +8,36 @@
* @copyright Copyright (c) 2012 - 2020 ApPHP Framework
* @license http://www.apphpframework.com/license/
*
- * PUBLIC (static): PROTECTED: PRIVATE:
+ * PUBLIC (static): PROTECTED: PRIVATE:
* ---------- ---------- ----------
* fileSize
- *
+ *
*/
class CConvert
{
-
- /**
- * Converts a given size into mb Mb or Kb
- * @param integer $size
- * @param array $params
- * @return float
- */
- public static function fileSize($size, $params = array())
- {
- $spaceBeforeUnit = isset($params['spaceBeforeUnit']) ? (bool)$params['spaceBeforeUnit'] : true;
- $unitCase = isset($params['unitCase']) ? $params['unitCase'] : '';
- $unit = array('b', 'kb', 'mb', 'gb', 'tb', 'pb');
-
- if ($unitCase == 'camel') {
- $unit = array('b', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb');
- } elseif ($unitCase == 'upper') {
- $unit = array('B', 'KB', 'MB', 'GB', 'TB', 'PB');
- }
-
- return @round($size / pow(1024, ($i = floor(log($size, 1024)))), 2) . ($spaceBeforeUnit ? ' ' : '') . $unit[$i];
- }
-
+
+ /**
+ * Converts a given size into mb Mb or Kb
+ *
+ * @param integer $size
+ * @param array $params
+ *
+ * @return float
+ */
+ public static function fileSize($size, $params = [])
+ {
+ $spaceBeforeUnit = isset($params['spaceBeforeUnit']) ? (bool)$params['spaceBeforeUnit'] : true;
+ $unitCase = isset($params['unitCase']) ? $params['unitCase'] : '';
+ $unit = ['b', 'kb', 'mb', 'gb', 'tb', 'pb'];
+
+ if ($unitCase == 'camel') {
+ $unit = ['b', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb'];
+ } elseif ($unitCase == 'upper') {
+ $unit = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
+ }
+
+ return @round($size / pow(1024, ($i = floor(log($size, 1024)))), 2).($spaceBeforeUnit ? ' ' : '').$unit[$i];
+ }
+
}
\ No newline at end of file
diff --git a/framework/helpers/CGeoLocation.php b/framework/helpers/CGeoLocation.php
index 8d31637..6f5c2fe 100644
--- a/framework/helpers/CGeoLocation.php
+++ b/framework/helpers/CGeoLocation.php
@@ -38,7 +38,7 @@ public static function coordinatesByAddress($address = '', $region = '', $key =
'®ion='.$region.
(!empty($key) ? '&key='.$key : '');
- $json = A::app()->getRequest()->getUrlContent($url, 'get', array(), array(), 'curl');
+ $json = A::app()->getRequest()->getUrlContent($url, 'get', [], [], 'curl');
$json = json_decode($json);
if(!empty($json) && $json->{'results'} != false){
diff --git a/framework/helpers/CHash.php b/framework/helpers/CHash.php
index 8aa78ab..f565eb1 100644
--- a/framework/helpers/CHash.php
+++ b/framework/helpers/CHash.php
@@ -109,9 +109,9 @@ public static function getRandomToken($length = 32)
* case: 'upper', 'lower' (default)
* @return string
*/
- public static function getRandomString($length = 10, $params = array())
- {
- $type = isset($params['type']) ? $params['type'] : '';
+ public static function getRandomString($length = 10, $params = [])
+ {
+ $type = isset($params['type']) ? $params['type'] : '';
$case = isset($params['case']) ? $params['case'] : '';
if ($type == 'numeric') {
@@ -234,9 +234,9 @@ private static function _padKey($key)
if (strlen($key) > 32) return false;
// Set sizes
- $sizes = array(16, 24, 32);
-
- // Loop through sizes and pad key
+ $sizes = [16, 24, 32];
+
+ // Loop through sizes and pad key
foreach ($sizes as $s) {
if ($s > strlen($key)) {
$key = str_pad($key, $s, "\0");
diff --git a/framework/helpers/CHtml.php b/framework/helpers/CHtml.php
index d7216ef..22c81d4 100644
--- a/framework/helpers/CHtml.php
+++ b/framework/helpers/CHtml.php
@@ -57,14 +57,14 @@
class CHtml
{
-
+
/** @const string */
const ID_PREFIX = 'ap';
/** @var string */
public static $afterRequiredLabel = '
* ';
/** @var string */
private static $_count = 0;
-
+
/**
* Generates an HTML tag
* @param string $tag
@@ -73,27 +73,27 @@ class CHtml
* @param boolean $closeTag
* @return string - HTML tag
*/
- public static function tag($tag, $htmlOptions = array(), $content = false, $closeTag = true)
- {
- $html = '<' . $tag . self::_renderAttributes($htmlOptions);
+ public static function tag($tag, $htmlOptions = [], $content = false, $closeTag = true)
+ {
+ $html = '<' . $tag . self::_renderAttributes($htmlOptions);
if ($content === false) {
return $closeTag ? $html . ' />' : $html . '>';
} else {
return $closeTag ? $html . '>' . $content . '' . $tag . '>' : $html . '>' . $content;
}
}
-
+
/**
* Generates an open HTML tag
* @param string $tag
* @param array $htmlOptions
* @return string - HTML tag
*/
- public static function openTag($tag, $htmlOptions = array())
- {
- return '<' . $tag . self::_renderAttributes($htmlOptions) . '>';
+ public static function openTag($tag, $htmlOptions = [])
+ {
+ return '<' . $tag . self::_renderAttributes($htmlOptions) . '>';
}
-
+
/**
* Generates a close HTML tag
* @param string $tag
@@ -103,7 +103,7 @@ public static function closeTag($tag)
{
return '' . $tag . '>';
}
-
+
/**
* Generates a hyperlink tag
* @param string $text
@@ -111,9 +111,9 @@ public static function closeTag($tag)
* @param array $htmlOptions
* @return string - HTML tag
*/
- public static function link($text, $url = '#', $htmlOptions = array())
- {
- if ($url !== '') $htmlOptions['href'] = $url;
+ public static function link($text, $url = '#', $htmlOptions = [])
+ {
+ if ($url !== '') $htmlOptions['href'] = $url;
if (isset($htmlOptions['escape']) && $htmlOptions['escape'] === true) {
$text = self::escapeHexEntity($text);
$htmlOptions['href'] = self::escapeHex($htmlOptions['href']);
@@ -125,7 +125,7 @@ public static function link($text, $url = '#', $htmlOptions = array())
}
return self::tag('a', $htmlOptions, $text);
}
-
+
/**
* Generates a label tag
* @param string $label
@@ -133,16 +133,16 @@ public static function link($text, $url = '#', $htmlOptions = array())
* @param array $htmlOptions
* @return string - HTML tag
*/
- public static function label($label, $for = false, $htmlOptions = array())
- {
- if ($for === false) {
+ public static function label($label, $for = false, $htmlOptions = [])
+ {
+ if ($for === false) {
if (isset($htmlOptions['for'])) unset($htmlOptions['for']);
} else {
$htmlOptions['for'] = $for;
}
return self::tag('label', $htmlOptions, $label);
}
-
+
/**
* Encodes special characters into HTML entities
* @param string $text
@@ -158,7 +158,7 @@ public static function encode($text, $flag = ENT_QUOTES)
return htmlspecialchars($text, $flag, A::app()->charset);
}
}
-
+
/**
* Decodes special HTML entities back to the corresponding characters
* @param string $text
@@ -169,7 +169,7 @@ public static function decode($text, $flag = ENT_QUOTES)
{
return htmlspecialchars_decode($text, $flag);
}
-
+
/**
* Encloses the passed CSS content with a CSS tag
* @param string $text
@@ -183,7 +183,7 @@ public static function css($text, $media = '', $newLine = true)
$newLine = (($newLine) ? "\n" : '');
return "";
}
-
+
/**
* Links to required CSS file
* @param string $url
@@ -196,22 +196,22 @@ public static function cssFile($url, $media = '', $newLine = true)
if ($media !== '') $media = ' media="' . $media . '"';
return '
' . (($newLine) ? "\n" : '');
}
-
+
/**
* Links to required CSS files
- * @param array $urls Usage: array('url1', 'url2' => array('media'=>'print'))
+ * @param array $urls Usage: ['url1', 'url2' => ['media'=>'print']]
* @param string $path
* @param bool $newLine
* @return string - HTML tag
*/
- public static function cssFiles($urls = array(), $path = '', $newLine = true)
- {
- $output = '';
-
+ public static function cssFiles($urls = [], $path = '', $newLine = true)
+ {
+ $output = '';
+
if (!is_array($urls)) {
return $output;
}
-
+
foreach ($urls as $key => $val) {
if (empty($val)) continue;
$path = !empty($path) ? trim($path, '/') . '/' : '';
@@ -219,10 +219,10 @@ public static function cssFiles($urls = array(), $path = '', $newLine = true)
$media = (is_array($val) && !empty($val['media'])) ? ' media="' . $val['media'] . '"' : '';
$output .= '
' . (($newLine) ? "\n" : '');
}
-
+
return $output;
}
-
+
/**
* Encloses the passed JavaScript within a Script tag
* @param string $text
@@ -232,7 +232,7 @@ public static function script($text)
{
return "";
}
-
+
/**
* Includes a JavaScript file
* @param string $url
@@ -241,9 +241,9 @@ public static function script($text)
* @param array $htmlOptions
* @return string - HTML tag
*/
- public static function scriptFile($url, $newLine = true, $preventDouble = false, $htmlOptions = array())
- {
- $include = false;
+ public static function scriptFile($url, $newLine = true, $preventDouble = false, $htmlOptions = [])
+ {
+ $include = false;
if ($preventDouble) {
$hash_name = md5($url);
if (!defined($hash_name)) {
@@ -253,7 +253,7 @@ public static function scriptFile($url, $newLine = true, $preventDouble = false,
} else {
$include = true;
}
-
+
if ($include) {
return '' . (($newLine) ? "\n" : '');
}
}
-
+
/**
* Links to required JavaScript files
- * @param array $urls Usage: array('url1', 'url2' => array('integrity'=>'...', 'crossorigin'=>'...'))
+ * @param array $urls Usage: ['url1', 'url2' => array['integrity'=>'...', 'crossorigin'=>'...']]
* @param string $path
* @param bool $newLine
* @return string - HTML tag
*/
- public static function scriptFiles($urls = array(), $path = '', $newLine = true)
- {
- $output = '';
-
+ public static function scriptFiles($urls = [], $path = '', $newLine = true)
+ {
+ $output = '';
+
if (!is_array($urls)) {
return $output;
}
-
+
foreach ($urls as $key => $val) {
$path = !empty($path) ? trim($path, '/') . '/' : '';
$href = is_array($val) ? $key : $val;
- $htmlOptions = is_array($val) ? $val : array();
- $output .= '' . (($newLine) ? "\n" : '');
}
-
+
return $output;
}
-
+
/**
* Generates an open form tag
* This is a shortcut to {@link openForm}
@@ -298,11 +298,11 @@ public static function scriptFiles($urls = array(), $path = '', $newLine = true)
* @param array $htmlOptions
* @return string
*/
- public static function form($action = '', $method = 'post', $htmlOptions = array())
- {
- return self::openForm($action, $method, $htmlOptions);
+ public static function form($action = '', $method = 'post', $htmlOptions = [])
+ {
+ return self::openForm($action, $method, $htmlOptions);
}
-
+
/**
* Generates an opening form tag
* Only the open tag is generated, a close tag should be placed manually at the end of the form
@@ -312,31 +312,31 @@ public static function form($action = '', $method = 'post', $htmlOptions = array
* @return string
* @see endForm
*/
- public static function openForm($action = '', $method = 'post', $htmlOptions = array())
- {
- $htmlOptions['action'] = $url = $action;
+ public static function openForm($action = '', $method = 'post', $htmlOptions = [])
+ {
+ $htmlOptions['action'] = $url = $action;
$htmlOptions['method'] = $method;
$form = self::tag('form', $htmlOptions, false, false);
- $hiddens = array();
- if (!strcasecmp($method, 'get') && ($pos = strpos($url, '?')) !== false) {
+ $hiddens = [];
+ if (!strcasecmp($method, 'get') && ($pos = strpos($url, '?')) !== false) {
foreach (explode('&', substr($url, $pos + 1)) as $pair) {
if (($pos = strpos($pair, '=')) !== false) {
- $hiddens[] = self::hiddenField(urldecode(substr($pair, 0, $pos)), urldecode(substr($pair, $pos + 1)), array('id' => false));
- }
+ $hiddens[] = self::hiddenField(urldecode(substr($pair, 0, $pos)), urldecode(substr($pair, $pos + 1)), ['id' => false]);
+ }
}
}
-
+
$request = A::app()->getRequest();
if ($request->getCsrfValidation() && !strcasecmp($method, 'post')) {
- $hiddens[] = self::hiddenField($request->getCsrfTokenKey(), $request->getCsrfTokenValue(), array('id' => false));
- }
- if ($hiddens !== array()) {
- $form .= "\n" . implode("\n", $hiddens) . "\n";
- }
-
- return $form;
+ $hiddens[] = self::hiddenField($request->getCsrfTokenKey(), $request->getCsrfTokenValue(), ['id' => false]);
+ }
+ if ($hiddens !== []) {
+ $form .= "\n".implode("\n", $hiddens)."\n";
+ }
+
+ return $form;
}
-
+
/**
* Generates a closing form tag
* @return string
@@ -346,7 +346,7 @@ public static function closeForm()
{
return '';
}
-
+
/**
* Generates a hidden input
* @param string $name
@@ -355,11 +355,11 @@ public static function closeForm()
* @return string
* @see inputField
*/
- public static function hiddenField($name, $value = '', $htmlOptions = array())
- {
- return self::_inputField('hidden', $name, $value, $htmlOptions) . "\n";
+ public static function hiddenField($name, $value = '', $htmlOptions = [])
+ {
+ return self::_inputField('hidden', $name, $value, $htmlOptions) . "\n";
}
-
+
/**
* Generates a textbox input
* @param string $name
@@ -368,11 +368,11 @@ public static function hiddenField($name, $value = '', $htmlOptions = array())
* @return string
* @see inputField
*/
- public static function textField($name, $value = '', $htmlOptions = array())
- {
- return self::_inputField('text', $name, $value, $htmlOptions);
+ public static function textField($name, $value = '', $htmlOptions = [])
+ {
+ return self::_inputField('text', $name, $value, $htmlOptions);
}
-
+
/**
* Generates a password field
* @param string $name
@@ -381,11 +381,11 @@ public static function textField($name, $value = '', $htmlOptions = array())
* @return string
* @see inputField
*/
- public static function passwordField($name, $value = '', $htmlOptions = array())
- {
- return self::_inputField('password', $name, $value, $htmlOptions);
+ public static function passwordField($name, $value = '', $htmlOptions = [])
+ {
+ return self::_inputField('password', $name, $value, $htmlOptions);
}
-
+
/**
* Generates a file field
* @param string $name
@@ -394,11 +394,11 @@ public static function passwordField($name, $value = '', $htmlOptions = array())
* @return string
* @see inputField
*/
- public static function fileField($name, $value = '', $htmlOptions = array())
- {
- return self::_inputField('file', $name, $value, $htmlOptions);
+ public static function fileField($name, $value = '', $htmlOptions = [])
+ {
+ return self::_inputField('file', $name, $value, $htmlOptions);
}
-
+
/**
* Generates a color input
* @param string $name
@@ -407,11 +407,11 @@ public static function fileField($name, $value = '', $htmlOptions = array())
* @return string
* @see inputField
*/
- public static function colorField($name, $value = '', $htmlOptions = array())
- {
- return self::_inputField('color', $name, $value, $htmlOptions);
+ public static function colorField($name, $value = '', $htmlOptions = [])
+ {
+ return self::_inputField('color', $name, $value, $htmlOptions);
}
-
+
/**
* Generates a email input
* @param string $name
@@ -420,11 +420,11 @@ public static function colorField($name, $value = '', $htmlOptions = array())
* @return string
* @see inputField
*/
- public static function emailField($name, $value = '', $htmlOptions = array())
- {
- return self::_inputField('email', $name, $value, $htmlOptions);
+ public static function emailField($name, $value = '', $htmlOptions = [])
+ {
+ return self::_inputField('email', $name, $value, $htmlOptions);
}
-
+
/**
* Generates a search input
* @param string $name
@@ -433,11 +433,11 @@ public static function emailField($name, $value = '', $htmlOptions = array())
* @return string
* @see inputField
*/
- public static function searchField($name, $value = '', $htmlOptions = array())
- {
- return self::_inputField('search', $name, $value, $htmlOptions);
+ public static function searchField($name, $value = '', $htmlOptions = [])
+ {
+ return self::_inputField('search', $name, $value, $htmlOptions);
}
-
+
/**
* Generates a valid HTML ID based on name
* @param string $name
@@ -445,10 +445,10 @@ public static function searchField($name, $value = '', $htmlOptions = array())
*/
public static function getIdByName($name)
{
- return str_replace(array('#', '[]', '][', '[', ']'), array('-', '', '_', '_', ''), $name);
- }
-
- /**
+ return str_replace(['#', '[]', '][', '[', ']'], ['-', '', '_', '_', ''], $name);
+ }
+
+ /**
* Generates an input HTML tag
* This method generates an input HTML tag based on the given name of input tag and value
* @param string $type
@@ -466,7 +466,7 @@ protected static function _inputField($type, $name, $value, $htmlOptions)
elseif ($htmlOptions['id'] === false) unset($htmlOptions['id']);
return self::tag('input', $htmlOptions, false);
}
-
+
/**
* Draws textarea
* @param string $name
@@ -474,14 +474,14 @@ protected static function _inputField($type, $name, $value, $htmlOptions)
* @param array $htmlOptions
* @return string
*/
- public static function textArea($name, $value = '', $htmlOptions = array())
- {
- $htmlOptions['name'] = $name;
+ public static function textArea($name, $value = '', $htmlOptions = [])
+ {
+ $htmlOptions['name'] = $name;
if (!isset($htmlOptions['id'])) $htmlOptions['id'] = self::getIdByName($name);
elseif ($htmlOptions['id'] === false) unset($htmlOptions['id']);
return self::tag('textarea', $htmlOptions, isset($htmlOptions['encode']) && !$htmlOptions['encode'] ? $value : self::encode($value));
}
-
+
/**
* Generates a check box
* @param string $name
@@ -489,40 +489,40 @@ public static function textArea($name, $value = '', $htmlOptions = array())
* @param array $htmlOptions
* @see inputField
*/
- public static function checkBox($name, $checked = false, $htmlOptions = array())
- {
- if ($checked) {
+ public static function checkBox($name, $checked = false, $htmlOptions = [])
+ {
+ if ($checked) {
$htmlOptions['checked'] = 'checked';
} elseif (isset($htmlOptions['checked'])) {
unset($htmlOptions['checked']);
}
-
+
$value = (isset($htmlOptions['value']) && $htmlOptions['value'] !== '') ? $htmlOptions['value'] : 1;
/// TODO self::_clientChange('click', $htmlOptions);
-
+
if (array_key_exists('uncheckValue', $htmlOptions)) {
$uncheck = $htmlOptions['uncheckValue'];
unset($htmlOptions['uncheckValue']);
} else {
$uncheck = null;
}
-
+
if ($uncheck !== null) {
// Add a hidden field so that if the checkbox is not selected, it still submits a value
if (isset($htmlOptions['id']) && $htmlOptions['id'] !== false) {
- $uncheckOptions = array('id' => self::ID_PREFIX . $htmlOptions['id']);
- } else {
- $uncheckOptions = array('id' => false);
- }
+ $uncheckOptions = ['id' => self::ID_PREFIX.$htmlOptions['id']];
+ } else {
+ $uncheckOptions = ['id' => false];
+ }
$hidden = self::hiddenField($name, $uncheck, $uncheckOptions);
} else {
$hidden = '';
}
-
+
// Add a hidden field so that if the checkbox is not selected, it still submits a value
return $hidden . self::_inputField('checkbox', $name, $value, $htmlOptions);
}
-
+
/**
* Generates a check box list
* @param string $name
@@ -531,42 +531,42 @@ public static function checkBox($name, $checked = false, $htmlOptions = array())
* @param array $htmlOptions
* @see tag
*/
- public static function checkBoxList($name, $select, $data, $htmlOptions = array())
- {
- $listWrapperTag = isset($htmlOptions['listWrapperTag']) ? $htmlOptions['listWrapperTag'] : 'span';
+ public static function checkBoxList($name, $select, $data, $htmlOptions = [])
+ {
+ $listWrapperTag = isset($htmlOptions['listWrapperTag']) ? $htmlOptions['listWrapperTag'] : 'span';
$listWrapperClass = isset($htmlOptions['listWrapperClass']) ? $htmlOptions['listWrapperClass'] : '';
$template = isset($htmlOptions['template']) ? $htmlOptions['template'] : '{input} {label}';
$separator = isset($htmlOptions['separator']) ? $htmlOptions['separator'] : "
\n";
$multiple = isset($htmlOptions['multiple']) ? (bool)$htmlOptions['multiple'] : true;
-
+
unset($htmlOptions['template'],
$htmlOptions['separator'],
$htmlOptions['listWrapperTag'],
$htmlOptions['listWrapperClass'],
$htmlOptions['multiple']);
-
+
if ($multiple && substr($name, -2) !== '[]') {
$name .= '[]';
}
-
+
// Get Check All option
if (isset($htmlOptions['checkAll'])) {
$checkAllLabel = $htmlOptions['checkAll'];
$checkAllLast = isset($htmlOptions['checkAllLast']) && $htmlOptions['checkAllLast'];
}
unset($htmlOptions['checkAll'], $htmlOptions['checkAllLast']);
-
- $labelOptions = array();
- if (isset($htmlOptions['labelOptions'])) {
+
+ $labelOptions = [];
+ if (isset($htmlOptions['labelOptions'])) {
$labelOptions = $htmlOptions['labelOptions'];
unset($htmlOptions['labelOptions']);
}
-
- $items = array();
- $baseID = self::getIdByName($name);
+
+ $items = [];
+ $baseID = self::getIdByName($name);
$id = 0;
$checkAll = true;
-
+
foreach ($data as $value => $label) {
$checked = !is_array($select) && !strcmp($value, $select) || is_array($select) && in_array($value, $select);
$checkAll = $checkAll && $checked;
@@ -574,46 +574,46 @@ public static function checkBoxList($name, $select, $data, $htmlOptions = array(
$htmlOptions['id'] = $baseID . '_' . $id++;
$option = self::checkBox($name, $checked, $htmlOptions);
$label = self::label($label, $htmlOptions['id'], $labelOptions);
- $items[] = strtr($template, array('{input}' => $option, '{label}' => $label));
- }
-
- if (isset($checkAllLabel)) {
+ $items[] = strtr($template, ['{input}' => $option, '{label}' => $label]);
+ }
+
+ if (isset($checkAllLabel)) {
$htmlOptions['value'] = 1;
$htmlOptions['id'] = $id = $baseID . '_all';
$option = self::checkBox($id, $checkAll, $htmlOptions);
$label = self::label($checkAllLabel, $id, $labelOptions);
- $item = strtr($template, array('{input}' => $option, '{label}' => $label));
- if ($checkAllLast) {
+ $item = strtr($template, ['{input}' => $option, '{label}' => $label]);
+ if ($checkAllLast) {
$items[] = $item;
} else {
array_unshift($items, $item);
}
- $name = strtr($name, array('[' => '\\[', ']' => '\\]'));
- $js = '$(\'#' . $id . '\').click(function() {$("input[name=\'' . $name . '\']").prop(\'checked\', this.checked);});';
+ $name = strtr($name, ['[' => '\\[', ']' => '\\]']);
+ $js = '$(\'#' . $id . '\').click(function() {$("input[name=\'' . $name . '\']").prop(\'checked\', this.checked);});';
$js .= '$("input[name=\'' . $name . '\']").click(function() {$(\'#' . $id . '\').prop(\'checked\', !$("input[name=\'' . $name . '\']:not(:checked)").length);});';
$js .= '$(\'#' . $id . '\').prop(\'checked\', !$("input[name=\'' . $name . '\']:not(:checked)").length);';
-
+
$clientScript = A::app()->getClientScript();
$clientScript->registerScript('Apphp.CHtml.#' . $id, $js);
}
-
- return self::tag($listWrapperTag, array('id' => $baseID, 'class' => $listWrapperClass), implode($separator, $items));
- }
-
- /**
+
+ return self::tag($listWrapperTag, ['id' => $baseID, 'class' => $listWrapperClass], implode($separator, $items));
+ }
+
+ /**
* Generates a radio button
* @param string $name
* @param boolean $checked
* @param array $htmlOptions
* @see inputField
*/
- public static function radioButton($name, $checked = false, $htmlOptions = array())
- {
- if ($checked) $htmlOptions['checked'] = 'checked';
+ public static function radioButton($name, $checked = false, $htmlOptions = [])
+ {
+ if ($checked) $htmlOptions['checked'] = 'checked';
elseif (isset($htmlOptions['checked'])) unset($htmlOptions['checked']);
-
+
$value = isset($htmlOptions['value']) ? $htmlOptions['value'] : 1;
-
+
/// TODO self::_clientChange('click', $htmlOptions);
if (array_key_exists('uncheckValue', $htmlOptions)) {
$uncheck = $htmlOptions['uncheckValue'];
@@ -621,21 +621,21 @@ public static function radioButton($name, $checked = false, $htmlOptions = array
} else {
$uncheck = null;
}
-
+
if ($uncheck !== null) {
// Add a hidden field (if radio button is not selected, it still will submit a value)
if (isset($htmlOptions['id']) && $htmlOptions['id'] !== false) {
- $uncheckOptions = array('id' => self::ID_PREFIX . $htmlOptions['id']);
- } else {
- $uncheckOptions = array('id' => false);
- }
- $hidden = self::hiddenField($name, $uncheck, $uncheckOptions);
+ $uncheckOptions = ['id' => self::ID_PREFIX.$htmlOptions['id']];
+ } else {
+ $uncheckOptions = ['id' => false];
+ }
+ $hidden = self::hiddenField($name, $uncheck, $uncheckOptions);
} else {
$hidden = '';
}
return $hidden . self::_inputField('radio', $name, $value, $htmlOptions);
}
-
+
/**
* Generates radio buttons list
* @param string $name
@@ -644,18 +644,18 @@ public static function radioButton($name, $checked = false, $htmlOptions = array
* @param array $htmlOptions
* @see tag
*/
- public static function radioButtonList($name, $select, $data, $htmlOptions = array())
- {
- $template = isset($htmlOptions['template']) ? $htmlOptions['template'] : '{input} {label}';
+ public static function radioButtonList($name, $select, $data, $htmlOptions = [])
+ {
+ $template = isset($htmlOptions['template']) ? $htmlOptions['template'] : '{input} {label}';
$separator = isset($htmlOptions['separator']) ? $htmlOptions['separator'] : "\n";
unset($htmlOptions['template'], $htmlOptions['separator']);
- $labelOptions = array();
- if (isset($htmlOptions['labelOptions'])) {
+ $labelOptions = [];
+ if (isset($htmlOptions['labelOptions'])) {
$labelOptions = $htmlOptions['labelOptions'];
unset($htmlOptions['labelOptions']);
}
- $items = array();
- $baseID = self::getIdByName($name);
+ $items = [];
+ $baseID = self::getIdByName($name);
$id = 0;
foreach ($data as $value => $label) {
$checked = !strcmp($value, $select);
@@ -663,12 +663,13 @@ public static function radioButtonList($name, $select, $data, $htmlOptions = arr
$htmlOptions['id'] = $baseID . '_' . $id++;
$option = self::radioButton($name, $checked, $htmlOptions);
$label = self::label($label, $htmlOptions['id'], $labelOptions);
- $items[] = strtr($template, array('{input}' => $option, '{label}' => $label));
- }
- return self::tag('span', array('id' => $baseID), implode($separator, $items));
- }
-
- /**
+ $items[] = strtr($template, ['{input}' => $option, '{label}' => $label]);
+ }
+
+ return self::tag('span', ['id' => $baseID], implode($separator, $items));
+ }
+
+ /**
* Draws dropdown list
* @param string $name
* @param mixed $select
@@ -677,18 +678,18 @@ public static function radioButtonList($name, $select, $data, $htmlOptions = arr
* @param array $specialOptions
* @return string
*/
- public static function dropDownList($name, $select = '', $data = array(), $htmlOptions = array(), $specialOptions = array())
- {
- $multiple = isset($htmlOptions['multiple']) ? (bool)$htmlOptions['multiple'] : false;
+ public static function dropDownList($name, $select = '', $data = [], $htmlOptions = [], $specialOptions = [])
+ {
+ $multiple = isset($htmlOptions['multiple']) ? (bool)$htmlOptions['multiple'] : false;
if ($multiple && substr($name, -2) !== '[]') {
$name .= '[]';
}
-
+
$htmlOptions['name'] = $name;
if (!isset($htmlOptions['id'])) $htmlOptions['id'] = self::getIdByName($name);
elseif ($htmlOptions['id'] === false) unset($htmlOptions['id']);
self::_clientChange('change', $htmlOptions);
-
+
$specialType = isset($specialOptions['type']) ? $specialOptions['type'] : '';
$specialStep = isset($specialOptions['step']) ? (int)$specialOptions['step'] : 1;
if ($specialType == 'hours') {
@@ -707,7 +708,7 @@ public static function dropDownList($name, $select = '', $data = array(), $htmlO
$options = "\n" . self::listOptions($select, $data, $htmlOptions);
return self::tag('select', $htmlOptions, $options);
}
-
+
/**
* Draws dropdown list
* @param string $name
@@ -716,15 +717,15 @@ public static function dropDownList($name, $select = '', $data = array(), $htmlO
* @param array $htmlOptions
* @return string
*/
- public static function listBox($name, $select = '', $data = array(), $htmlOptions = array())
- {
- if (!isset($htmlOptions['size'])) $htmlOptions['size'] = 4;
+ public static function listBox($name, $select = '', $data = [], $htmlOptions = [])
+ {
+ if (!isset($htmlOptions['size'])) $htmlOptions['size'] = 4;
if (isset($htmlOptions['multiple'])) {
if (substr($name, -2) !== '[]') $name .= '[]';
}
return self::dropDownList($name, $select, $data, $htmlOptions);
}
-
+
/**
* Generates the list of options
* @param mixed $selection
@@ -733,31 +734,33 @@ public static function listBox($name, $select = '', $data = array(), $htmlOption
* @return string
*
* Usage:
- * $array=>('0'=>'Option A', '1'=>'Option B', '2'=>'Option C');
- * $array=>('0'=>'Option A', '1'=>'Option B', '2'=>array('optionValue'=>'Option C', 'optionDisabled'=>true));
+ * $array=>['0'=>'Option A', '1'=>'Option B', '2'=>'Option C'];
+ * $array=>['0'=>'Option A', '1'=>'Option B', '2'=>['optionValue'=>'Option C', 'optionDisabled'=>true]];
*/
public static function listOptions($selection, $listData, &$htmlOptions)
{
$raw = isset($htmlOptions['encode']) && !$htmlOptions['encode'];
$content = '';
if (isset($htmlOptions['prompt'])) {
- $content .= '
' . strtr($htmlOptions['prompt'], array('<' => '<', '>' => '>')) . " \n";
- unset($htmlOptions['prompt']);
- }
- if (isset($htmlOptions['empty'])) {
- if (!is_array($htmlOptions['empty'])) $htmlOptions['empty'] = array('' => $htmlOptions['empty']);
- foreach ($htmlOptions['empty'] as $value => $label) {
- $content .= '
' . strtr($label, array('<' => '<', '>' => '>')) . " \n";
- }
+ $content .= '
'.strtr($htmlOptions['prompt'], ['<' => '<', '>' => '>'])." \n";
+ unset($htmlOptions['prompt']);
+ }
+ if (isset($htmlOptions['empty'])) {
+ if ( ! is_array($htmlOptions['empty'])) {
+ $htmlOptions['empty'] = ['' => $htmlOptions['empty']];
+ }
+ foreach ($htmlOptions['empty'] as $value => $label) {
+ $content .= '
'.strtr($label, ['<' => '<', '>' => '>'])." \n";
+ }
unset($htmlOptions['empty']);
}
if (isset($htmlOptions['options'])) {
$options = $htmlOptions['options'];
unset($htmlOptions['options']);
} else {
- $options = array();
- }
- $key = isset($htmlOptions['key']) ? $htmlOptions['key'] : 'primaryKey';
+ $options = [];
+ }
+ $key = isset($htmlOptions['key']) ? $htmlOptions['key'] : 'primaryKey';
if (is_array($selection)) {
foreach ($selection as $i => $item) {
if (is_object($item)) $selection[$i] = $item->$key;
@@ -770,8 +773,8 @@ public static function listOptions($selection, $listData, &$htmlOptions)
if (is_array($value)) {
if (isset($value['optionValue'])) {
// For single-level arrays where additional options available
- $attributes = array('value' => (string)$key, 'encode' => !$raw);
- if (!empty($value['optionDisabled'])) $attributes['disabled'] = true;
+ $attributes = ['value' => (string)$key, 'encode' => ! $raw];
+ if (!empty($value['optionDisabled'])) $attributes['disabled'] = true;
if (!is_array($selection) && !strcmp($key, $selection) || is_array($selection) && in_array($key, $selection)) {
$attributes['selected'] = 'selected';
}
@@ -780,14 +783,14 @@ public static function listOptions($selection, $listData, &$htmlOptions)
} else {
// For multi-level arrays
$content .= '
\n";
- $dummy = array('options' => $options);
- if (isset($htmlOptions['encode'])) $dummy['encode'] = $htmlOptions['encode'];
+ $dummy = ['options' => $options];
+ if (isset($htmlOptions['encode'])) $dummy['encode'] = $htmlOptions['encode'];
$content .= self::listOptions($selection, $value, $dummy);
$content .= ' ' . "\n";
}
} else {
- $attributes = array('value' => (string)$key, 'encode' => !$raw);
- if (!is_array($selection) && !strcmp($key, $selection) || is_array($selection) && in_array($key, $selection)) {
+ $attributes = ['value' => (string)$key, 'encode' => ! $raw];
+ if (!is_array($selection) && !strcmp($key, $selection) || is_array($selection) && in_array($key, $selection)) {
$attributes['selected'] = 'selected';
}
if (isset($options[$key])) $attributes = array_merge($attributes, $options[$key]);
@@ -797,50 +800,50 @@ public static function listOptions($selection, $listData, &$htmlOptions)
if (isset($htmlOptions['key'])) unset($htmlOptions['key']);
return $content;
}
-
+
/**
* Draws submit button
* @param string $label
* @param array $htmlOptions
* @return string
*/
- public static function submitButton($label = 'submit', $htmlOptions = array())
- {
- $htmlOptions['type'] = 'submit';
+ public static function submitButton($label = 'submit', $htmlOptions = [])
+ {
+ $htmlOptions['type'] = 'submit';
return self::button($label, $htmlOptions);
}
-
+
/**
* Generates reset button
* @param string $label
* @param array $htmlOptions
* @return string
*/
- public static function resetButton($label = 'reset', $htmlOptions = array())
- {
- $htmlOptions['type'] = 'reset';
+ public static function resetButton($label = 'reset', $htmlOptions = [])
+ {
+ $htmlOptions['type'] = 'reset';
return self::button($label, $htmlOptions);
}
-
+
/**
* Draws button
* @param string $label
* @param array $htmlOptions
* @return string
*/
- public static function button($label = 'button', $htmlOptions = array())
- {
- if (!isset($htmlOptions['name'])) {
+ public static function button($label = 'button', $htmlOptions = [])
+ {
+ if (!isset($htmlOptions['name'])) {
if (!array_key_exists('name', $htmlOptions)) $htmlOptions['name'] = self::ID_PREFIX . self::$_count++;
}
-
+
if (!isset($htmlOptions['type'])) $htmlOptions['type'] = 'button';
$buttonTag = 'input';
if (isset($htmlOptions['buttonTag'])) {
$buttonTag = $htmlOptions['buttonTag'];
unset($htmlOptions['buttonTag']);
}
-
+
if ($buttonTag == 'button') {
if (isset($htmlOptions['value'])) {
$buttonValue = $htmlOptions['value'];
@@ -855,7 +858,7 @@ public static function button($label = 'button', $htmlOptions = array())
return self::tag('input', $htmlOptions);
}
}
-
+
/**
* Generates an image tag
* @param string $src
@@ -863,33 +866,33 @@ public static function button($label = 'button', $htmlOptions = array())
* @param array $htmlOptions
* @return string
*/
- public static function image($src, $alt = '', $htmlOptions = array())
- {
- $htmlOptions['src'] = $src;
+ public static function image($src, $alt = '', $htmlOptions = [])
+ {
+ $htmlOptions['src'] = $src;
$htmlOptions['alt'] = $alt;
return self::tag('img', $htmlOptions);
}
-
+
/**
* Generates an video tag
* @param string $src
* @param array $options
- * Ex.: array('width'=>'560', 'height'=>'350', 'autoplay'=>true, 'allowfullscreen'=>true, 'controls'=>true)
+ * Ex.: ['width'=>'560', 'height'=>'350', 'autoplay'=>true, 'allowfullscreen'=>true, 'controls'=>true]
* @return string
*/
- public static function video($src, $options = array())
- {
- $videoHtml = '';
-
+ public static function video($src, $options = [])
+ {
+ $videoHtml = '';
+
$srcParts = explode('/', $src);
$videoId = array_pop($srcParts);
-
+
$htmlOptions = array();
$width = !empty($options['width']) ? $options['width'] : '560';
$htmlOptions['width'] = $width;
$height = !empty($options['height']) ? $options['height'] : '315';
$htmlOptions['height'] = $height;
-
+
if (preg_match('/(youtube\.|youtu\.)/i', $src)) {
$autoplayParam = !empty($options['autoplay']) ? '?autoplay=1' : '';
$htmlOptions['frameborder'] = '0';
@@ -911,34 +914,34 @@ public static function video($src, $options = array())
$videoHtml .= self::tag('source', array('src' => $src, 'type' => 'video/mp4'));
$videoHtml .= self::closeTag('video');
}
-
+
if (!empty($htmlOptions)) {
$videoHtml = self::openTag('iframe', $htmlOptions, false, false) . self::closeTag('iframe');
}
-
+
return $videoHtml;
}
-
+
/**
* Generates an audio tag
* @param string $src
* @param array $options
- * Ex.: array('autoplay'=>true, 'controls'=>true)
+ * Ex.: ['autoplay'=>true, 'controls'=>true]
* @return string
*/
- public static function audio($src, $options = array())
- {
- $htmlOptions = array();
- $htmlOptions['autoplay'] = !empty($options['autoplay']) ? ' autoplay' : null;
- $htmlOptions['controls'] = !empty($options['controls']) ? ' controls' : null;
-
- $audioHtml = self::openTag('audio', $htmlOptions);
- $audioHtml .= self::tag('source', array('src' => $src, 'type' => 'audio/mpeg'));
- $audioHtml .= self::closeTag('audio');
-
- return $audioHtml;
+ public static function audio($src, $options = [])
+ {
+ $htmlOptions = [];
+ $htmlOptions['autoplay'] = ! empty($options['autoplay']) ? ' autoplay' : null;
+ $htmlOptions['controls'] = ! empty($options['controls']) ? ' controls' : null;
+
+ $audioHtml = self::openTag('audio', $htmlOptions);
+ $audioHtml .= self::tag('source', ['src' => $src, 'type' => 'audio/mpeg']);
+ $audioHtml .= self::closeTag('audio');
+
+ return $audioHtml;
}
-
+
/**
* Returns a file size in bytes from the given string
* @param mixed $fileSize
@@ -957,7 +960,7 @@ public static function convertFileSize($fileSize)
}
return $return;
}
-
+
/**
* Returns an image width or height in pixels from the given string
* @param mixed $fileDimension
@@ -972,7 +975,7 @@ public static function convertImageDimensions($fileDimension)
}
return $return;
}
-
+
/**
* Renders escaped hex string
* Ex. for link: '
... '
@@ -986,7 +989,7 @@ public static function escapeHex($string)
}
return $return;
}
-
+
/**
* Renders escaped hex entity string
* Ex. for text: '
'.CHtml::escapeHexEntity($string).' '
@@ -1000,7 +1003,7 @@ public static function escapeHexEntity($string)
}
return $return;
}
-
+
/**
* Generates JavaScript code with specified client changes
* @param string $event
@@ -1011,37 +1014,37 @@ protected static function _clientChange($event, &$htmlOptions)
if (!isset($htmlOptions['submit'])) {
return;
}
-
+
$clientScript = A::app()->getClientScript();
$request = A::app()->getRequest();
$handler = '';
-
+
if (isset($htmlOptions['id'])) {
$id = $htmlOptions['id'];
} else {
$id = $htmlOptions['id'] = isset($htmlOptions['name']) ? $htmlOptions['name'] : self::ID_PREFIX . self::$_count++;
}
-
+
$csrf = isset($htmlOptions['csrf']) ? (bool)$htmlOptions['csrf'] : false;
-
- // Add csrf token key if needed
+
+ // Add csrf token key if needed
if ($request->getCsrfValidation() && $csrf) {
$handler .= '$(this).closest("form").append(\'
\');';
}
-
+
if (!empty($htmlOptions['submit']) && !is_bool($htmlOptions['submit'])) {
$handler .= $htmlOptions['submit'];
}
-
+
/// Check? document.forms["'.$formName.'"].submit();';
$handler .= '$(this).closest("form").submit();';
-
+
$clientScript->registerScript('Apphp.CHtml.#' . $id, "$('body').on('$event','#$id',function(){{$handler}});");
/// Check? $clientScript->registerScript('Apphp.CHtml.#'.$id, "$('#$id').on('$event', function(){{$handler}});");
-
+
unset($htmlOptions['submit']);
}
-
+
/**
* Renders the HTML tag attributes
* @param string $htmlOptions
@@ -1049,7 +1052,7 @@ protected static function _clientChange($event, &$htmlOptions)
private static function _renderAttributes($htmlOptions)
{
// Attributes that looks like attribute = "attribute"
- static $specialAttributes = array(
+ static $specialAttributes = [
'checked' => 1,
'declare' => 1,
'defer' => 1,
@@ -1061,24 +1064,26 @@ private static function _renderAttributes($htmlOptions)
'readonly' => 1,
'selected' => 1,
'autofocus' => 1,
- );
-
- if ($htmlOptions === array()) return '';
-
- $output = '';
+ ];
+
+ if ($htmlOptions === []) {
+ return '';
+ }
+
+ $output = '';
$encode = false;
-
+
if (isset($htmlOptions['encode'])) {
$encode = (bool)$htmlOptions['encode'];
unset($htmlOptions['encode']);
}
-
+
if (isset($htmlOptions['id']) && $htmlOptions['id'] === false) unset($htmlOptions['id']);
if (isset($htmlOptions['href']) && $htmlOptions['href'] === false) unset($htmlOptions['href']);
if (isset($htmlOptions['class']) && $htmlOptions['class'] == '') unset($htmlOptions['class']);
if (isset($htmlOptions['style']) && $htmlOptions['style'] == '') unset($htmlOptions['style']);
if (isset($htmlOptions['showAlways'])) unset($htmlOptions['showAlways']);
-
+
if (is_array($htmlOptions)) {
foreach ($htmlOptions as $name => $value) {
if (isset($specialAttributes[$name])) {
@@ -1088,8 +1093,8 @@ private static function _renderAttributes($htmlOptions)
}
}
}
-
+
return $output;
}
-
+
}
diff --git a/framework/helpers/CImage.php b/framework/helpers/CImage.php
index ac562b6..f6d3dec 100644
--- a/framework/helpers/CImage.php
+++ b/framework/helpers/CImage.php
@@ -87,6 +87,10 @@ public static function resizeImage($imagePath, $imageName, $resizeWidth = '', $r
$imagePathNameNew = $imagePath . $imageName;
if ($case != '') {
+ // Prevent using of size like 150px or something else
+ $resizeWidth = intval($resizeWidth);
+ $resizeHeight = intval($resizeHeight);
+
if ($resizeWidth != '' && $resizeHeight == '') {
$newWidth = $resizeWidth;
$newHeight = ($height / $width) * $newWidth;
diff --git a/framework/helpers/CLocale.php b/framework/helpers/CLocale.php
index 19eb169..faf9a58 100644
--- a/framework/helpers/CLocale.php
+++ b/framework/helpers/CLocale.php
@@ -20,361 +20,376 @@
class CLocale
{
-
- protected static $_arrDateFormats = array(
- 'Y-m-d' => array('preview' => '[ Y-m-d ]', 'converted_format' => '%Y-%m-%d'),
- 'm-d-Y' => array('preview' => '[ m-d-Y ]', 'converted_format' => '%m-%d-%Y'),
- 'd-m-Y' => array('preview' => '[ d-m-Y ]', 'converted_format' => '%d-%m-%Y'),
-
- 'Y M d' => array('preview' => '[ Y M d ]', 'converted_format' => '%Y %M %d'),
- 'M d Y' => array('preview' => '[ M d Y ]', 'converted_format' => '%M %d %Y'),
- 'd M Y' => array('preview' => '[ d M Y ]', 'converted_format' => '%d %M %Y'),
- 'M d, Y' => array('preview' => '[ M d, Y ]', 'converted_format' => '%M %d, %Y'),
- 'd M, Y' => array('preview' => '[ d M, Y ]', 'converted_format' => '%d %M, %Y'),
-
- 'Y M j' => array('preview' => '[ Y M j ]', 'converted_format' => '%Y %M %j'),
- 'M j, Y' => array('preview' => '[ M j, Y ]', 'converted_format' => '%M %j, %Y'),
- 'j M, Y' => array('preview' => '[ j M, Y ]', 'converted_format' => '%j %M, %Y'),
-
- 'Y F d' => array('preview' => '[ Y F d ]', 'converted_format' => '%Y %F %d'),
- 'F d Y' => array('preview' => '[ F d Y ]', 'converted_format' => '%F %d %Y'),
- 'd F Y' => array('preview' => '[ d F Y ]', 'converted_format' => '%d %F %Y'),
-
- 'Y F j' => array('preview' => '[ Y F j ]', 'converted_format' => '%Y %F %j'),
- 'F j, Y' => array('preview' => '[ F j, Y ]', 'converted_format' => '%F %j, %Y'),
- 'j F, Y' => array('preview' => '[ j F, Y ]', 'converted_format' => '%j %F, %Y'),
- );
-
- protected static $_arrTimeFormats = array(
- 'H:i:s' => array('preview' => '[ H:i:s ]', 'converted_format' => '%H:%i:%s'),
- 'h:i:s' => array('preview' => '[ h:i:s ]', 'converted_format' => '%h:%i:%s'),
- 'g:i:s' => array('preview' => '[ g:i:s ]', 'converted_format' => '%g:%i:%s'),
-
- 'h:i a' => array('preview' => '[ h:i a ]', 'converted_format' => '%h:%i %a'),
- 'h:i A' => array('preview' => '[ h:i A ]', 'converted_format' => '%h:%i %A'),
- 'g:i a' => array('preview' => '[ g:i a ]', 'converted_format' => '%g:%i %a'),
- 'g:i A' => array('preview' => '[ g:i A ]', 'converted_format' => '%g:%i %A'),
- );
-
- protected static $_arrShortTimeFormats = array(
- 'H:i' => array('preview' => '[ H:i ]', 'converted_format' => '%H:%i'),
- 'h:i' => array('preview' => '[ h:i ]', 'converted_format' => '%h:%i'),
- 'g:i' => array('preview' => '[ g:i ]', 'converted_format' => '%g:%i'),
- );
-
- protected static $_arrDateTimeFormats = array(
- 'Y-m-d H:i:s' => array('preview' => '[ Y-m-d H:i:s ] ', 'converted_format' => '%Y-%m-%d %H:%i:%s'),
- 'm-d-Y H:i:s' => array('preview' => '[ m-d-Y H:i:s ] ', 'converted_format' => '%m-%d-%Y %H:%i:%s'),
- 'd-m-Y H:i:s' => array('preview' => '[ d-m-Y H:i:s ] ', 'converted_format' => '%d-%m-%Y %H:%i:%s'),
- 'm-d-Y h:i:s' => array('preview' => '[ m-d-Y H:i:s ] ', 'converted_format' => '%m-%d-%Y %h:%i:%s'),
- 'd-m-Y h:i:s' => array('preview' => '[ d-m-Y H:i:s ] ', 'converted_format' => '%d-%m-%Y %h:%i:%s'),
- 'm-d-Y g:ia' => array('preview' => '[ m-d-Y g:ia ] ', 'converted_format' => '%m-%d-%Y %g:%i%a'),
- 'd-m-Y g:ia' => array('preview' => '[ d-m-Y g:ia ] ', 'converted_format' => '%d-%m-%Y %g:%i%a'),
- 'M d, Y g:ia' => array('preview' => '[ M d, Y g:ia ] ', 'converted_format' => '%M %d, %Y %g:%i%a'),
- 'd M, Y g:ia' => array('preview' => '[ d M, Y g:ia ] ', 'converted_format' => '%d %M, %Y %g:%i%a'),
- 'F j Y, g:ia' => array('preview' => '[ F j Y, g:ia ] ', 'converted_format' => '%F %j %Y, %g:%i%a'),
- 'j F Y, g:ia' => array('preview' => '[ j F Y, g:ia ] ', 'converted_format' => '%j %F %Y, %g:%i%a'),
- 'D, F j Y g:ia' => array('preview' => '[ D, F j Y g:ia ] ', 'converted_format' => '%D, %F %j %Y %g:%i%a'),
- 'D, M d Y g:ia' => array('preview' => '[ D, M d Y g:ia ] ', 'converted_format' => '%D, %M %d %Y %g:%i%a'),
- );
-
- /**
- * Transforms the given date into localazed date
- * @param string $format
- * @param string $date
- * @param bool $unixFormat
- * @return string
- */
- public static function date($format = '', $date = '', $unixFormat = false)
- {
- $dateFormat = null;
- $search = array();
- $replace = array();
- $result = '';
- $amPm = '';
-
- if ($unixFormat) {
- $date = !empty($date) ? date('Y-m-d H:i:s', $date) : date('Y-m-d H:i:s');
- } else {
- $date = !empty($date) ? $date : date('Y-m-d H:i:s');
- }
-
- if (isset(self::$_arrDateTimeFormats[$format])) {
- $dateFormat = self::$_arrDateTimeFormats[$format];
- $parts = explode(' ', $date);
-
- $dateParts = isset($parts[0]) ? explode('-', $parts[0]) : array();
- $year = isset($dateParts[0]) ? $dateParts[0] : '';
- $month = isset($dateParts[1]) ? $dateParts[1] : '';
- $day = isset($dateParts[2]) ? $dateParts[2] : '';
- $weekDay = date('w', strtotime($date)) + 1;
-
- $timeParts = isset($parts[1]) ? explode(':', $parts[1]) : array();
- $hour = isset($timeParts[0]) ? $timeParts[0] : '';
- $hour24 = $hour;
- $hour12 = ($hour >= 13 ? $hour - 12 : $hour);
- $minute = isset($timeParts[1]) ? $timeParts[1] : '';
- $second = isset($timeParts[2]) ? $timeParts[2] : '';
-
- $amPm = ($hour24 < 12) ? A::t('i18n', 'amName') : A::t('i18n', 'pmName');
-
- $convertedFormat = isset($dateFormat['converted_format']) ? $dateFormat['converted_format'] : '';
- } elseif (isset(self::$_arrDateFormats[$format])) {
- $dateFormat = self::$_arrDateFormats[$format];
-
- $parts = explode(' ', $date);
- $dateParts = isset($parts[0]) ? explode('-', $parts[0]) : array();
-
- $year = isset($dateParts[0]) ? $dateParts[0] : '';
- $month = isset($dateParts[1]) ? $dateParts[1] : '';
- $day = isset($dateParts[2]) ? $dateParts[2] : '';
- $dayParts = explode(' ', $day);
- $day = isset($day[0]) ? $dayParts[0] : '';
-
- $convertedFormat = isset($dateFormat['converted_format']) ? $dateFormat['converted_format'] : '';
- } elseif (isset(self::$_arrTimeFormats[$format])) {
- $dateFormat = self::$_arrTimeFormats[$format];
-
- if (strlen($date) > 8) {
- $parts = explode(' ', $date);
- $timeParts = isset($parts[1]) ? explode(':', $parts[1]) : array();
- } else {
- $timeParts = explode(':', $date);
- }
-
- $hour = isset($timeParts[0]) ? $timeParts[0] : '';
- $hour24 = $hour;
- $hour12 = ($hour >= 13 ? $hour - 12 : $hour);
- $minute = isset($timeParts[1]) ? $timeParts[1] : '';
- $second = isset($timeParts[2]) ? $timeParts[2] : '';
-
- $amPm = ($hour24 < 12) ? A::t('i18n', 'amName') : A::t('i18n', 'pmName');
-
- $convertedFormat = isset($dateFormat['converted_format']) ? $dateFormat['converted_format'] : '';
- } elseif (isset(self::$_arrShortTimeFormats[$format])) {
- $dateFormat = self::$_arrShortTimeFormats[$format];
-
- if (strlen($date) > 5) {
- $parts = explode(' ', $date);
- $timeParts = isset($parts[1]) ? explode(':', $parts[1]) : array();
- } else {
- $timeParts = explode(':', $date);
- }
-
- $hour = isset($timeParts[0]) ? $timeParts[0] : '';
- $hour24 = $hour;
- $hour12 = ($hour >= 13 ? $hour - 12 : $hour);
- $minute = isset($timeParts[1]) ? $timeParts[1] : '';
-
- $convertedFormat = isset($dateFormat['converted_format']) ? $dateFormat['converted_format'] : '';
- } else {
- $result = date($format, strtotime($date));
- }
-
- if ($dateFormat) {
-
- switch ($format) {
-
- /*
- |---------------------------------------------------
- | Date Formats
- |---------------------------------------------------
- */
- case 'Y-m-d': /* 2015-01-31 */
- case 'm-d-Y': /* 01-31-2015 */
- case 'd-m-Y': /* 31-01-2015 */
-
- $search = array('%Y', '%m', '%d');
- $replace = array($year, $month, $day);
- break;
-
- case 'Y M d': /* 2015 Oct 01 */
- case 'M d Y': /* Oct 01 2015 */
- case 'd M Y': /* 01 Oct 2015 */
- case 'M d, Y': /* Oct 01, 2015 */
- case 'd M, Y': /* 01 Oct, 2015 */
-
- $search = array('%Y', '%M', '%d');
- $replace = array($year, A::t('i18n', 'monthNames.abbreviated.' . (int)$month), $day);
- break;
-
- case 'Y M j': /* 2015 Oct 1 */
- case 'M j, Y': /* Oct 1, 2015 */
- case 'j M, Y': /* 1 Oct, 2015 */
-
- $search = array('%Y', '%M', '%j');
- $replace = array($year, A::t('i18n', 'monthNames.abbreviated.' . (int)$month), (int)$day);
- break;
-
- case 'Y F d': /* 2015 October 01 */
- case 'F d Y': /* October 01 2015 */
- case 'd F Y': /* 01 October 2015 */
-
- $search = array('%Y', '%F', '%d');
- $replace = array($year, A::t('i18n', 'monthNames.wide.' . (int)$month), $day);
- break;
-
- case 'Y F j': /* 2015 October 1 */
- case 'F j, Y': /* October 1, 2015 */
- case 'j F, Y': /* 1 October, 2015 */
-
- $search = array('%Y', '%F', '%j');
- $replace = array($year, A::t('i18n', 'monthNames.wide.' . (int)$month), (int)$day);
- break;
-
- /*
- |---------------------------------------------------
- | Time Formats
- |---------------------------------------------------
- */
- case 'H:i:s': /* 13:53:20 */
- case 'h:i:s': /* 01:53:20 */
- case 'g:i:s': /* 1:53:20 */
-
- $search = array('%H', '%h', '%g', '%i', '%s');
- $replace = array($hour24, $hour12, (int)$hour12, $minute, $second);
- break;
-
- case 'h:i a': /* 01:47 pm */
- case 'g:i a': /* 1:47 pm */
-
- $search = array('%h', '%g', '%i', '%a');
- $replace = array($hour12, (int)$hour12, $minute, strtolower($amPm));
- break;
-
- case 'h:i A': /* 01:47 PM */
- case 'g:i A': /* 1:47 PM */
-
- $search = array('%h', '%g', '%i', '%A');
- $replace = array($hour12, (int)$hour12, $minute, strtoupper($amPm));
- break;
-
- case 'H:i': /* 13:47 */
- case 'h:i': /* 01:47 */
- case 'g:i': /* 1:47 */
-
- $search = array('%H', '%h', '%g', '%i');
- $replace = array($hour24, $hour12, (int)$hour12, $minute);
- break;
-
- /*
- |---------------------------------------------------
- | DateTime Formats
- |---------------------------------------------------
- */
- case 'Y-m-d H:i:s': /* 2015-01-31 13:02:59 */
- case 'm-d-Y H:i:s': /* 01-31-2015 13:02:59 */
- case 'd-m-Y H:i:s': /* 31-01-2015 13:02:59 */
- case 'm-d-Y h:i:s': /* 01-31-2015 01:02:59 */
- case 'd-m-Y h:i:s': /* 31-01-2015 01:02:59 */
-
- $search = array('%Y', '%m', '%d', '%H', '%h', '%g', '%i', '%s');
- $replace = array($year, $month, $day, $hour24, $hour12, (int)$hour12, $minute, $second);
- break;
-
- case 'm-d-Y g:ia': /* 2015-01-31 1:02pm */
- case 'd-m-Y g:ia': /* 31-01-2015 1:02pm */
-
- $search = array('%Y', '%m', '%d', '%g', '%i', '%a');
- $replace = array($year, $month, $day, (int)$hour12, $minute, strtolower($amPm));
- break;
-
- case 'M d, Y g:ia': /* Oct 09, 2015 1:02pm */
- case 'd M, Y g:ia': /* 09 Oct, 2015 1:02pm */
-
- $monthAbbrev = A::t('i18n', 'monthNames.abbreviated.' . (int)$month);
- $search = array('%Y', '%M', '%d', '%g', '%i', '%a');
- $replace = array($year, $monthAbbrev, $day, (int)$hour12, $minute, strtolower($amPm));
- break;
-
- case 'F j Y, g:ia': /* October 1 2015, 1:02pm */
- case 'j F Y, g:ia': /* 1 October 2015, 1:02pm */
-
- $monthWide = A::t('i18n', 'monthNames.wide.' . (int)$month);
- $search = array('%Y', '%F', '%j', '%g', '%i', '%a');
- $replace = array($year, $monthWide, (int)$day, (int)$hour12, $minute, strtolower($amPm));
- break;
-
- case 'D, F j Y g:ia': /* Mon, October 1 2015 1:02pm */
- case 'D, M d Y g:ia': /* Mon, Oct 1 2015 1:02pm */
-
- $monthWide = A::t('i18n', 'monthNames.wide.' . (int)$month);
- $monthAbbrev = A::t('i18n', 'monthNames.abbreviated.' . (int)$month);
- $weekDayAbbrev = A::t('i18n', 'weekDayNames.abbreviated.' . (int)$weekDay);
- $search = array('%Y', '%F', '%M', '%j', '%d', '%D', '%g', '%i', '%a');
- $replace = array($year, $monthWide, $monthAbbrev, (int)$day, $day, $weekDayAbbrev, (int)$hour12, $minute, strtolower($amPm));
- break;
-
- default:
- $result = $date;
- break;
- }
-
- if (!empty($search) && !empty($replace)) {
- $result = str_replace($search, $replace, $convertedFormat);
- }
- }
-
- return $result;
- }
-
- /**
- * Returns array of datetime formats supported by system
- * @return array
- */
- public static function getDateTimeFormats()
- {
- $result = array();
-
- foreach (self::$_arrDateTimeFormats as $key => $dateTimeFormat) {
- $result[$key] = $dateTimeFormat['preview'];
- }
-
- return $result;
- }
-
- /**
- * Returns array of date formats supported by system
- * @return array
- */
- public static function getDateFormats()
- {
- $result = array();
-
- foreach (self::$_arrDateFormats as $key => $dateFormat) {
- $result[$key] = $dateFormat['preview'];
- }
-
- return $result;
- }
-
- /**
- * Returns array of time formats supported by system
- * @return array
- */
- public static function getTimeFormats()
- {
- $result = array();
-
- foreach (self::$_arrTimeFormats as $key => $timeFormat) {
- $result[$key] = $timeFormat['preview'];
- }
-
- return $result;
- }
-
- /**
- * Returns array of short time formats supported by system
- * @return array
- */
- public static function getShortTimeFormats()
- {
- $result = array();
-
- foreach (self::$_arrShortTimeFormats as $key => $timeFormat) {
- $result[$key] = $timeFormat['preview'];
- }
-
- return $result;
- }
+
+ protected static $_arrDateFormats = [
+ 'Y-m-d' => ['preview' => '[ Y-m-d ]', 'converted_format' => '%Y-%m-%d'],
+ 'm-d-Y' => ['preview' => '[ m-d-Y ]', 'converted_format' => '%m-%d-%Y'],
+ 'd-m-Y' => ['preview' => '[ d-m-Y ]', 'converted_format' => '%d-%m-%Y'],
+
+ 'Y M d' => ['preview' => '[ Y M d ]', 'converted_format' => '%Y %M %d'],
+ 'M d Y' => ['preview' => '[ M d Y ]', 'converted_format' => '%M %d %Y'],
+ 'd M Y' => ['preview' => '[ d M Y ]', 'converted_format' => '%d %M %Y'],
+ 'M d, Y' => ['preview' => '[ M d, Y ]', 'converted_format' => '%M %d, %Y'],
+ 'd M, Y' => ['preview' => '[ d M, Y ]', 'converted_format' => '%d %M, %Y'],
+
+ 'Y M j' => ['preview' => '[ Y M j ]', 'converted_format' => '%Y %M %j'],
+ 'M j, Y' => ['preview' => '[ M j, Y ]', 'converted_format' => '%M %j, %Y'],
+ 'j M, Y' => ['preview' => '[ j M, Y ]', 'converted_format' => '%j %M, %Y'],
+
+ 'Y F d' => ['preview' => '[ Y F d ]', 'converted_format' => '%Y %F %d'],
+ 'F d Y' => ['preview' => '[ F d Y ]', 'converted_format' => '%F %d %Y'],
+ 'd F Y' => ['preview' => '[ d F Y ]', 'converted_format' => '%d %F %Y'],
+
+ 'Y F j' => ['preview' => '[ Y F j ]', 'converted_format' => '%Y %F %j'],
+ 'F j, Y' => ['preview' => '[ F j, Y ]', 'converted_format' => '%F %j, %Y'],
+ 'j F, Y' => ['preview' => '[ j F, Y ]', 'converted_format' => '%j %F, %Y'],
+ ];
+
+ protected static $_arrTimeFormats = [
+ 'H:i:s' => ['preview' => '[ H:i:s ]', 'converted_format' => '%H:%i:%s'],
+ 'h:i:s' => ['preview' => '[ h:i:s ]', 'converted_format' => '%h:%i:%s'],
+ 'g:i:s' => ['preview' => '[ g:i:s ]', 'converted_format' => '%g:%i:%s'],
+
+ 'h:i a' => ['preview' => '[ h:i a ]', 'converted_format' => '%h:%i %a'],
+ 'h:i A' => ['preview' => '[ h:i A ]', 'converted_format' => '%h:%i %A'],
+ 'g:i a' => ['preview' => '[ g:i a ]', 'converted_format' => '%g:%i %a'],
+ 'g:i A' => ['preview' => '[ g:i A ]', 'converted_format' => '%g:%i %A'],
+ ];
+
+ protected static $_arrShortTimeFormats
+ = [
+ 'H:i' => ['preview' => '[ H:i ]', 'converted_format' => '%H:%i'],
+ 'h:i' => ['preview' => '[ h:i ]', 'converted_format' => '%h:%i'],
+ 'g:i' => ['preview' => '[ g:i ]', 'converted_format' => '%g:%i'],
+ ];
+
+ protected static $_arrDateTimeFormats
+ = [
+ 'Y-m-d H:i:s' => ['preview' => '[ Y-m-d H:i:s ] ', 'converted_format' => '%Y-%m-%d %H:%i:%s'],
+ 'm-d-Y H:i:s' => ['preview' => '[ m-d-Y H:i:s ] ', 'converted_format' => '%m-%d-%Y %H:%i:%s'],
+ 'd-m-Y H:i:s' => ['preview' => '[ d-m-Y H:i:s ] ', 'converted_format' => '%d-%m-%Y %H:%i:%s'],
+ 'm-d-Y h:i:s' => ['preview' => '[ m-d-Y H:i:s ] ', 'converted_format' => '%m-%d-%Y %h:%i:%s'],
+ 'd-m-Y h:i:s' => ['preview' => '[ d-m-Y H:i:s ] ', 'converted_format' => '%d-%m-%Y %h:%i:%s'],
+ 'm-d-Y g:ia' => ['preview' => '[ m-d-Y g:ia ] ', 'converted_format' => '%m-%d-%Y %g:%i%a'],
+ 'd-m-Y g:ia' => ['preview' => '[ d-m-Y g:ia ] ', 'converted_format' => '%d-%m-%Y %g:%i%a'],
+ 'M d, Y g:ia' => ['preview' => '[ M d, Y g:ia ] ', 'converted_format' => '%M %d, %Y %g:%i%a'],
+ 'd M, Y g:ia' => ['preview' => '[ d M, Y g:ia ] ', 'converted_format' => '%d %M, %Y %g:%i%a'],
+ 'F j Y, g:ia' => ['preview' => '[ F j Y, g:ia ] ', 'converted_format' => '%F %j %Y, %g:%i%a'],
+ 'j F Y, g:ia' => ['preview' => '[ j F Y, g:ia ] ', 'converted_format' => '%j %F %Y, %g:%i%a'],
+ 'D, F j Y g:ia' => ['preview' => '[ D, F j Y g:ia ] ', 'converted_format' => '%D, %F %j %Y %g:%i%a'],
+ 'D, M d Y g:ia' => ['preview' => '[ D, M d Y g:ia ] ', 'converted_format' => '%D, %M %d %Y %g:%i%a'],
+ ];
+
+ /**
+ * Transforms the given date into localazed date
+ *
+ * @param string $format
+ * @param string $date
+ * @param bool $unixFormat
+ * @return string
+ */
+ public static function date($format = '', $date = '', $unixFormat = false)
+ {
+ $dateFormat = null;
+ $search = [];
+ $replace = [];
+ $result = '';
+ $amPm = '';
+
+ if ($unixFormat) {
+ $date = ! empty($date) ? date('Y-m-d H:i:s', $date) : date('Y-m-d H:i:s');
+ } else {
+ $date = ! empty($date) ? $date : date('Y-m-d H:i:s');
+ }
+
+ if (isset(self::$_arrDateTimeFormats[$format])) {
+ $dateFormat = self::$_arrDateTimeFormats[$format];
+ $parts = explode(' ', $date);
+
+ $dateParts = isset($parts[0]) ? explode('-', $parts[0]) : [];
+ $year = isset($dateParts[0]) ? $dateParts[0] : '';
+ $month = isset($dateParts[1]) ? $dateParts[1] : '';
+ $day = isset($dateParts[2]) ? $dateParts[2] : '';
+ $weekDay = date('w', strtotime($date)) + 1;
+
+ $timeParts = isset($parts[1]) ? explode(':', $parts[1]) : [];
+ $hour = isset($timeParts[0]) ? $timeParts[0] : '';
+ $hour24 = $hour;
+ $hour12 = ($hour >= 13 ? $hour - 12 : $hour);
+ $minute = isset($timeParts[1]) ? $timeParts[1] : '';
+ $second = isset($timeParts[2]) ? $timeParts[2] : '';
+
+ $amPm = ($hour24 < 12) ? A::t('i18n', 'amName') : A::t('i18n', 'pmName');
+
+ $convertedFormat = isset($dateFormat['converted_format']) ? $dateFormat['converted_format'] : '';
+ } elseif (isset(self::$_arrDateFormats[$format])) {
+ $dateFormat = self::$_arrDateFormats[$format];
+
+ $parts = explode(' ', $date);
+ $dateParts = isset($parts[0]) ? explode('-', $parts[0]) : [];
+
+ $year = isset($dateParts[0]) ? $dateParts[0] : '';
+ $month = isset($dateParts[1]) ? $dateParts[1] : '';
+ $day = isset($dateParts[2]) ? $dateParts[2] : '';
+ $dayParts = explode(' ', $day);
+ $day = isset($day[0]) ? $dayParts[0] : '';
+
+ $convertedFormat = isset($dateFormat['converted_format']) ? $dateFormat['converted_format'] : '';
+ } elseif (isset(self::$_arrTimeFormats[$format])) {
+ $dateFormat = self::$_arrTimeFormats[$format];
+
+ if (strlen($date) > 8) {
+ $parts = explode(' ', $date);
+ $timeParts = isset($parts[1]) ? explode(':', $parts[1]) : [];
+ } else {
+ $timeParts = explode(':', $date);
+ }
+
+ $hour = isset($timeParts[0]) ? $timeParts[0] : '';
+ $hour24 = $hour;
+ $hour12 = ($hour >= 13 ? $hour - 12 : $hour);
+ $minute = isset($timeParts[1]) ? $timeParts[1] : '';
+ $second = isset($timeParts[2]) ? $timeParts[2] : '';
+
+ $amPm = ($hour24 < 12) ? A::t('i18n', 'amName') : A::t('i18n', 'pmName');
+
+ $convertedFormat = isset($dateFormat['converted_format']) ? $dateFormat['converted_format'] : '';
+ } elseif (isset(self::$_arrShortTimeFormats[$format])) {
+ $dateFormat = self::$_arrShortTimeFormats[$format];
+
+ if (strlen($date) > 5) {
+ $parts = explode(' ', $date);
+ $timeParts = isset($parts[1]) ? explode(':', $parts[1]) : [];
+ } else {
+ $timeParts = explode(':', $date);
+ }
+
+ $hour = isset($timeParts[0]) ? $timeParts[0] : '';
+ $hour24 = $hour;
+ $hour12 = ($hour >= 13 ? $hour - 12 : $hour);
+ $minute = isset($timeParts[1]) ? $timeParts[1] : '';
+
+ $convertedFormat = isset($dateFormat['converted_format']) ? $dateFormat['converted_format'] : '';
+ } else {
+ $result = date($format, strtotime($date));
+ }
+
+ if ($dateFormat) {
+ switch ($format) {
+ /*
+ |---------------------------------------------------
+ | Date Formats
+ |---------------------------------------------------
+ */
+ case 'Y-m-d': /* 2015-01-31 */
+ case 'm-d-Y': /* 01-31-2015 */
+ case 'd-m-Y': /* 31-01-2015 */
+
+ $search = ['%Y', '%m', '%d'];
+ $replace = [$year, $month, $day];
+ break;
+
+ case 'Y M d': /* 2015 Oct 01 */
+ case 'M d Y': /* Oct 01 2015 */
+ case 'd M Y': /* 01 Oct 2015 */
+ case 'M d, Y': /* Oct 01, 2015 */
+ case 'd M, Y': /* 01 Oct, 2015 */
+
+ $search = ['%Y', '%M', '%d'];
+ $replace = [$year, A::t('i18n', 'monthNames.abbreviated.'.(int)$month), $day];
+ break;
+
+ case 'Y M j': /* 2015 Oct 1 */
+ case 'M j, Y': /* Oct 1, 2015 */
+ case 'j M, Y': /* 1 Oct, 2015 */
+
+ $search = ['%Y', '%M', '%j'];
+ $replace = [$year, A::t('i18n', 'monthNames.abbreviated.'.(int)$month), (int)$day];
+ break;
+
+ case 'Y F d': /* 2015 October 01 */
+ case 'F d Y': /* October 01 2015 */
+ case 'd F Y': /* 01 October 2015 */
+
+ $search = ['%Y', '%F', '%d'];
+ $replace = [$year, A::t('i18n', 'monthNames.wide.'.(int)$month), $day];
+ break;
+
+ case 'Y F j': /* 2015 October 1 */
+ case 'F j, Y': /* October 1, 2015 */
+ case 'j F, Y': /* 1 October, 2015 */
+
+ $search = ['%Y', '%F', '%j'];
+ $replace = [$year, A::t('i18n', 'monthNames.wide.'.(int)$month), (int)$day];
+ break;
+
+ /*
+ |---------------------------------------------------
+ | Time Formats
+ |---------------------------------------------------
+ */
+ case 'H:i:s': /* 13:53:20 */
+ case 'h:i:s': /* 01:53:20 */
+ case 'g:i:s': /* 1:53:20 */
+
+ $search = ['%H', '%h', '%g', '%i', '%s'];
+ $replace = [$hour24, $hour12, (int)$hour12, $minute, $second];
+ break;
+
+ case 'h:i a': /* 01:47 pm */
+ case 'g:i a': /* 1:47 pm */
+
+ $search = ['%h', '%g', '%i', '%a'];
+ $replace = [$hour12, (int)$hour12, $minute, strtolower($amPm)];
+ break;
+
+ case 'h:i A': /* 01:47 PM */
+ case 'g:i A': /* 1:47 PM */
+
+ $search = ['%h', '%g', '%i', '%A'];
+ $replace = [$hour12, (int)$hour12, $minute, strtoupper($amPm)];
+ break;
+
+ case 'H:i': /* 13:47 */
+ case 'h:i': /* 01:47 */
+ case 'g:i': /* 1:47 */
+
+ $search = ['%H', '%h', '%g', '%i'];
+ $replace = [$hour24, $hour12, (int)$hour12, $minute];
+ break;
+
+ /*
+ |---------------------------------------------------
+ | DateTime Formats
+ |---------------------------------------------------
+ */
+ case 'Y-m-d H:i:s': /* 2015-01-31 13:02:59 */
+ case 'm-d-Y H:i:s': /* 01-31-2015 13:02:59 */
+ case 'd-m-Y H:i:s': /* 31-01-2015 13:02:59 */
+ case 'm-d-Y h:i:s': /* 01-31-2015 01:02:59 */
+ case 'd-m-Y h:i:s': /* 31-01-2015 01:02:59 */
+
+ $search = ['%Y', '%m', '%d', '%H', '%h', '%g', '%i', '%s'];
+ $replace = [$year, $month, $day, $hour24, $hour12, (int)$hour12, $minute, $second];
+ break;
+
+ case 'm-d-Y g:ia': /* 2015-01-31 1:02pm */
+ case 'd-m-Y g:ia': /* 31-01-2015 1:02pm */
+
+ $search = ['%Y', '%m', '%d', '%g', '%i', '%a'];
+ $replace = [$year, $month, $day, (int)$hour12, $minute, strtolower($amPm)];
+ break;
+
+ case 'M d, Y g:ia': /* Oct 09, 2015 1:02pm */
+ case 'd M, Y g:ia': /* 09 Oct, 2015 1:02pm */
+
+ $monthAbbrev = A::t('i18n', 'monthNames.abbreviated.'.(int)$month);
+ $search = ['%Y', '%M', '%d', '%g', '%i', '%a'];
+ $replace = [$year, $monthAbbrev, $day, (int)$hour12, $minute, strtolower($amPm)];
+ break;
+
+ case 'F j Y, g:ia': /* October 1 2015, 1:02pm */
+ case 'j F Y, g:ia': /* 1 October 2015, 1:02pm */
+
+ $monthWide = A::t('i18n', 'monthNames.wide.'.(int)$month);
+ $search = ['%Y', '%F', '%j', '%g', '%i', '%a'];
+ $replace = [$year, $monthWide, (int)$day, (int)$hour12, $minute, strtolower($amPm)];
+ break;
+
+ case 'D, F j Y g:ia': /* Mon, October 1 2015 1:02pm */
+ case 'D, M d Y g:ia': /* Mon, Oct 1 2015 1:02pm */
+
+ $monthWide = A::t('i18n', 'monthNames.wide.'.(int)$month);
+ $monthAbbrev = A::t('i18n', 'monthNames.abbreviated.'.(int)$month);
+ $weekDayAbbrev = A::t('i18n', 'weekDayNames.abbreviated.'.(int)$weekDay);
+ $search = ['%Y', '%F', '%M', '%j', '%d', '%D', '%g', '%i', '%a'];
+ $replace = [
+ $year,
+ $monthWide,
+ $monthAbbrev,
+ (int)$day,
+ $day,
+ $weekDayAbbrev,
+ (int)$hour12,
+ $minute,
+ strtolower($amPm)
+ ];
+ break;
+
+ default:
+ $result = $date;
+ break;
+ }
+
+ if ( ! empty($search) && ! empty($replace)) {
+ $result = str_replace($search, $replace, $convertedFormat);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns array of datetime formats supported by system
+ *
+ * @return array
+ */
+ public static function getDateTimeFormats()
+ {
+ $result = [];
+
+ foreach (self::$_arrDateTimeFormats as $key => $dateTimeFormat) {
+ $result[$key] = $dateTimeFormat['preview'];
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns array of date formats supported by system
+ *
+ * @return array
+ */
+ public static function getDateFormats()
+ {
+ $result = [];
+
+ foreach (self::$_arrDateFormats as $key => $dateFormat) {
+ $result[$key] = $dateFormat['preview'];
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns array of time formats supported by system
+ *
+ * @return array
+ */
+ public static function getTimeFormats()
+ {
+ $result = [];
+
+ foreach (self::$_arrTimeFormats as $key => $timeFormat) {
+ $result[$key] = $timeFormat['preview'];
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns array of short time formats supported by system
+ *
+ * @return array
+ */
+ public static function getShortTimeFormats()
+ {
+ $result = [];
+
+ foreach (self::$_arrShortTimeFormats as $key => $timeFormat) {
+ $result[$key] = $timeFormat['preview'];
+ }
+
+ return $result;
+ }
}
diff --git a/framework/helpers/COauth.php b/framework/helpers/COauth.php
index 636ee92..6f6fe66 100644
--- a/framework/helpers/COauth.php
+++ b/framework/helpers/COauth.php
@@ -26,71 +26,72 @@
class COauth
{
/* @var */
- private static $config = array(
- 'path' => '/',
- 'callback_url' => '',
- 'security_salt' => '',
- 'security_iteration' => 300,
- 'security_timeout' => '2 minutes',
-
- 'callback_transport' => 'session',
- 'debug' => false,
- 'Strategy' => array(
- 'Facebook' => array(
- 'app_id' => '',
- 'app_secret' => '',
- 'scope' => 'public_profile,email',
- 'fields' => 'id,name,first_name,last_name,gender,email',
- ),
- 'Google' => array(
- 'client_id' => '',
- 'client_secret' => '',
- 'scope' => 'email',
- ),
- 'Twitter' => array(
- 'key' => '',
- 'secret' => '',
- 'scope' => 'include_email',
- ),
- 'LinkedIn' => array(
- 'api_key' => '',
- 'secret_key' => '',
- 'scope' => 'r_basicprofile r_emailaddress',
- ),
- ),
- );
-
- /**
- * Sets a basic configuration
+ private static $config
+ = [
+ 'path' => '/',
+ 'callback_url' => '',
+ 'security_salt' => '',
+ 'security_iteration' => 300,
+ 'security_timeout' => '2 minutes',
+
+ 'callback_transport' => 'session',
+ 'debug' => false,
+ 'Strategy' => [
+ 'Facebook' => [
+ 'app_id' => '',
+ 'app_secret' => '',
+ 'scope' => 'public_profile,email',
+ 'fields' => 'id,name,first_name,last_name,gender,email',
+ ],
+ 'Google' => [
+ 'client_id' => '',
+ 'client_secret' => '',
+ 'scope' => 'email',
+ ],
+ 'Twitter' => [
+ 'key' => '',
+ 'secret' => '',
+ 'scope' => 'include_email',
+ ],
+ 'LinkedIn' => [
+ 'api_key' => '',
+ 'secret_key' => '',
+ 'scope' => 'r_basicprofile r_emailaddress',
+ ],
+ ],
+ ];
+
+ /**
+ * Sets a basic configuration
* More information - https://github.com/opauth/opauth/wiki/Opauth-configuration
* @param array $params
- * Usage:
- * COauth::config(array(
- * 'path' => '/customer/login/type/', // If url address: http://my_site/customer/login/type/facebook
- * 'callback_url' => 'http://my_site/customer/success_return',
- * 'security_salt' => '{random_string}',
- * 'security_iteration' => '300',
- * 'security_timeout' => '2 minutes',
- * 'callback_transport' => 'session', // It can take the following parameters: 'session', 'post' or 'get';
- * 'debug' => false,
- * 'Strategy' => array(
- * 'Facebook' => array(
- * 'app_id' => '{application_id}',
- * 'app_secret' => '{application_secret}',
- * 'score' => 'public_profile,email', // More - https://developers.facebook.com/docs/facebook-login/permissions
- * 'fields' => 'id,name,first_name,last_name,gender,email' // More (look fields) - https://developers.facebook.com/docs/graph-api/reference/v2.6/user
- * ),
- * 'Google' => array(
- * 'client_id' => '{application_id}',
- * 'client_secret' => '{application_secret}',
- * 'score' => 'email', // More - https://developers.google.com/+/web/api/rest/oauth#authorization-scopes
- * ),
- * 'Twitter' => array(
- * 'key' => '{application_id}',
- * 'secret' => '{application_secret}'
- * )
- * ),
- * ))
+ * Usage:
+ * COauth::config([
+ * 'path' => '/customer/login/type/', // If url address: http://my_site/customer/login/type/facebook
+ * 'callback_url' => 'http://my_site/customer/success_return',
+ * 'security_salt' => '{random_string}',
+ * 'security_iteration' => '300',
+ * 'security_timeout' => '2 minutes',
+ * 'callback_transport' => 'session', // It can take the following parameters: 'session', 'post' or 'get';
+ * 'debug' => false,
+ * 'Strategy' => [
+ * 'Facebook' => [
+ * 'app_id' => '{application_id}',
+ * 'app_secret' => '{application_secret}',
+ * 'score' => 'public_profile,email', // More - https://developers.facebook.com/docs/facebook-login/permissions
+ * 'fields' => 'id,name,first_name,last_name,gender,email' // More (look fields) - https://developers.facebook.com/docs/graph-api/reference/v2.6/user
+ * ],
+ * 'Google' => [
+ * 'client_id' => '{application_id}',
+ * 'client_secret' => '{application_secret}',
+ * 'score' => 'email', // More - https://developers.google.com/+/web/api/rest/oauth#authorization-scopes
+ * ],
+ * 'Twitter' => [
+ * 'key' => '{application_id}',
+ * 'secret' => '{application_secret}'
+ * ]
+ * ],
+ * ])
*
* @return void
*/
diff --git a/framework/helpers/CRss.php b/framework/helpers/CRss.php
index cfce726..a2a4291 100644
--- a/framework/helpers/CRss.php
+++ b/framework/helpers/CRss.php
@@ -42,7 +42,7 @@
* #$rss_text = htmlentities($post_text, ENT_COMPAT, 'UTF-8');
* self::SetItem(APPHP_BASE.'index.php?page=news&nid='.$allNews[0][$i]['id'], $allNews[0][$i]['header_text'], $rss_text, $allNews[0][$i]['date_created']);
* }
- * News::UpdateFields(array('rss_last_ids'=>$rss_ids));
+ * News::UpdateFields(['rss_last_ids'=>$rss_ids]);
* }
*
* self::SaveFeed();
@@ -63,12 +63,12 @@ class CRss
private static $_channelSubject = '';
private static $_rssType = 'rss1';
- private static $_rssTypes = array('rss1', 'rss2', 'atom');
-
- private static $_imageUrl = '';
+ private static $_rssTypes = ['rss1', 'rss2', 'atom'];
+
+ private static $_imageUrl = '';
- private static $_arrItems = array();
- private static $_countItems = 0;
+ private static $_arrItems = [];
+ private static $_countItems = 0;
private static $_filePath = 'feeds/';
private static $_fileName = 'rss.xml';
@@ -87,9 +87,9 @@ public static function setType($type = '')
* Sets Channel
* @param array $params
*/
- public static function setChannel($params = array())
- {
- // $creator, $subject
+ public static function setChannel($params = [])
+ {
+ // $creator, $subject
self::$_channelUrl = isset($params['url']) ? $params['url'] : '';
self::$_channelTitle = isset($params['title']) ? $params['title'] : '';
self::$_channelDescription = isset($params['description']) ? $params['description'] : '';
@@ -266,10 +266,14 @@ public static function saveFeed()
@fclose($handle);
$result = '';
} else {
- $result = A::t('core', 'Cannot open RSS file to add a new item! Please check your access rights to {file} or try again later.', array('{file}' => self::$_filePath . self::$_fileName));
- }
-
- return $result;
+ $result = A::t(
+ 'core',
+ 'Cannot open RSS file to add a new item! Please check your access rights to {file} or try again later.',
+ ['{file}' => self::$_filePath.self::$_fileName]
+ );
+ }
+
+ return $result;
}
/**
diff --git a/framework/helpers/CSecureHeaders.php b/framework/helpers/CSecureHeaders.php
new file mode 100644
index 0000000..17a7db9
--- /dev/null
+++ b/framework/helpers/CSecureHeaders.php
@@ -0,0 +1,97 @@
+
+ * @link http://www.apphpframework.com/
+ * @copyright Copyright (c) 2012 - 2020 ApPHP Framework
+ * @license http://www.apphpframework.com/license/
+ *
+ * PUBLIC (static): PROTECTED: PRIVATE:
+ * ---------- ---------- ----------
+ * renderHeaders
+ *
+ */
+
+class CSecureHeaders
+{
+ /**
+ * Render headers
+ *
+ * @return mixed
+ */
+ public static function renderHeaders()
+ {
+ /*
+ |-------------------------------------------------------------------------------
+ | Prevent browsers from incorrectly detecting non-scripts as scripts
+ | SEE MORE: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
+ |-------------------------------------------------------------------------------
+ */
+ header('X-Content-Type-Options: nosniff');
+
+ /*
+ |-------------------------------------------------------------------------------
+ | Only allow my site to frame itself
+ | ------------------------
+ | Other options:
+ | header('X-Frame-Options', 'ALLOW FROM https://example.com/')
+ | SEE MORE: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
+ |-------------------------------------------------------------------------------
+ */
+ header('X-Frame-Options: SAMEORIGIN');
+
+ /*
+ |-------------------------------------------------------------------------------
+ | Block pages from loading when they detect reflected XSS attacks
+ | SEE MORE: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
+ |-------------------------------------------------------------------------------
+ */
+ header('X-XSS-Protection: 1; mode=block');
+
+ /*
+ |-------------------------------------------------------------------------------
+ | Only connect to this site via HTTPS for the two years
+ | ------------------------
+ | Other options:
+ | header('Strict-Transport-Security', 'max-age=63072000; includeSubDomains; preload')
+ | SEE MORE: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
+ |-------------------------------------------------------------------------------
+ */
+ header('Strict-Transport-Security: max-age=63072000');
+
+ /*
+ |-------------------------------------------------------------------------------
+ | Will not allow any information to be sent when a scheme downgrade happens (the user is navigating from HTTPS to HTTP)
+ | ------------------------
+ | Other options:
+ | SEE MORE: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
+ |-------------------------------------------------------------------------------
+ */
+ header('Referrer-Policy: strict-origin-when-cross-origin');
+
+ /*
+ |-------------------------------------------------------------------------------
+ | Allow or deny the use of browser features in its own frame, and in content within any