+ $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 . ')
+
+ ▲
+ ▼
+ ☐
+ ▣
+ ×
@@ -467,13 +485,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') . '. ';
From 23a89d20477d3b8ccdb31be187ee764a6afa4753 Mon Sep 17 00:00:00 2001
From: Samuel Akopyan <>
Date: Fri, 1 May 2020 23:23:55 +0300
Subject: [PATCH 15/45] Fix for european float number format validation
---
framework/helpers/widgets/CDataForm.php | 15 ++++++++++-----
framework/helpers/widgets/CFormValidation.php | 4 ++--
2 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/framework/helpers/widgets/CDataForm.php b/framework/helpers/widgets/CDataForm.php
index d213652..d5a4a17 100644
--- a/framework/helpers/widgets/CDataForm.php
+++ b/framework/helpers/widgets/CDataForm.php
@@ -978,11 +978,16 @@ private static function _setValidationAttributes(&$htmlOptions, $validationRules
$htmlOptions['data-validation-error-msg'] = A::t('core', 'The field {title} must be a valid HTML element size value (ex.: 100px, pt, em or %)! Please re-enter.', array('{title}' => $title));
break;
case 'float':
- $format_sample = ($validationFormat == 'european') ? '1234,00' : '1234.00';
- $htmlOptions['data-validation'] = 'number';
- $htmlOptions['data-validation-allowing'] = 'float,negative';
- $htmlOptions['data-validation-error-msg'] = A::t('core', 'The field {title} must be a valid float value in format: {format}! Please re-enter.', array('{title}' => $title, '{format}' => $format_sample));
- break;
+ if ($validationFormat == 'european') {
+ $formatSample = '1234,00';
+ $htmlOptions['data-validation-decimal-separator'] = ',';
+ }else{
+ $formatSample = '1234.00';
+ }
+ $htmlOptions['data-validation'] = 'number';
+ $htmlOptions['data-validation-allowing'] = 'float,negative';
+ $htmlOptions['data-validation-error-msg'] = A::t('core', 'The field {title} must be a valid float value in format: {format}! Please re-enter.', array('{title}' => $title, '{format}' => $formatSample));
+ break;
case 'url':
//$htmlOptions['data-validation-regexp'] = '^(http:\/\/|https:\/\/|ftp:\/\/)$';
$htmlOptions['data-validation'] = 'url';
diff --git a/framework/helpers/widgets/CFormValidation.php b/framework/helpers/widgets/CFormValidation.php
index 05ee12a..a53afc7 100644
--- a/framework/helpers/widgets/CFormValidation.php
+++ b/framework/helpers/widgets/CFormValidation.php
@@ -407,8 +407,8 @@ private static function _handleField($field, $fieldInfo, $isMultiArray = false,
break;
case 'float':
$valid = CValidator::isFloat($fieldValue, $format);
- $format_sample = ($format == 'european') ? '1234,00' : '1234.00';
- $errorMessage = A::t($msgSource, 'The field {title} must be a valid float value in format: {format}! Please re-enter.', array('{title}' => $title, '{format}' => $format_sample));
+ $formatSample = ($format == 'european') ? '1234,00' : '1234.00';
+ $errorMessage = A::t($msgSource, 'The field {title} must be a valid float value in format: {format}! Please re-enter.', array('{title}' => $title, '{format}' => $formatSample));
if ($valid && $minValue != '') {
$valid = CValidator::validateMin($fieldValue, $minValue, $format);
$errorMessage = A::t($msgSource, 'The field {title} must be greater than or equal to {min}! Please re-enter.', array('{title}' => $title, '{min}' => $minValue));
From a729ce4426948de1845ef8ea6aca40487cfe0db3 Mon Sep 17 00:00:00 2001
From: Samuel Akopyan <>
Date: Sat, 16 May 2020 18:22:44 +0300
Subject: [PATCH 16/45] Added possibility to hide system queries in debug panel
---
CHANGELOG | 1 +
framework/core/CDebug.php | 11 +++++++++--
framework/db/CDatabase.php | 18 +++++++++---------
3 files changed, 19 insertions(+), 11 deletions(-)
diff --git a/CHANGELOG b/CHANGELOG
index 073e195..f08bb95 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,6 @@
Version 1.4.x -
----------------------------
+- Enh: added possibility to hide system queries in debug panel
- Bug: fixed wrong assignment of _isRendered in CView
diff --git a/framework/core/CDebug.php b/framework/core/CDebug.php
index 81cfad2..8d2ffe7 100644
--- a/framework/core/CDebug.php
+++ b/framework/core/CDebug.php
@@ -409,6 +409,8 @@ public static function displayInfo()
var arrDebugTabs = ["General","Params","Console","Warnings","Errors","Queries"];
var debugTabsHeight = "200px";
var cssText = keyTab = "";
+
+ function toggleSystemQueries(){var x, i; x = document.getElementsByClassName("dbugQuery");for(i = 0; i < x.length; i++){if(x[i].style.display === "none"){x[i].style.display = "";}else {x[i].style.display = "none";}}}
function appSetCookie(state, tab){ document.cookie = "debugBarState="+state+"; path=/"; if(tab !== null) document.cookie = "debugBarTab="+tab+"; path=/"; }
function appGetCookie(name){ if(document.cookie.length > 0){ start_c = document.cookie.indexOf(name + "="); if(start_c != -1){ start_c += (name.length + 1); end_c = document.cookie.indexOf(";", start_c); if(end_c == -1) end_c = document.cookie.length; return unescape(document.cookie.substring(start_c,end_c)); }} return ""; }
function appTabsMiddle(){ appExpandTabs("middle", appGetCookie("debugBarTab")); }
@@ -663,11 +665,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 .= '
diff --git a/framework/db/CDatabase.php b/framework/db/CDatabase.php
index 2f32096..367a310 100644
--- a/framework/db/CDatabase.php
+++ b/framework/db/CDatabase.php
@@ -236,7 +236,7 @@ public function select($sql, $params = array(), $method = 'fetchAll', $fetchMode
$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);
+ 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;
@@ -301,7 +301,7 @@ public function insert($table, $data, $forceUpdate = false)
$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);
+ CDebug::addMessage('queries', '' . ++self::$count . '. insert | ' . $sqlTotalTime . ' ' . A::t('core', 'sec') . '. | ID: ' . (($result) ? $result : '0 (error )') . ' ', $this->_query);
}
return $result;
@@ -385,7 +385,7 @@ public function update($table, $data, $where = '1', $params = array(), $forceUpd
$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);
+ CDebug::addMessage('queries', ''.++self::$count . '. update | ' . $sqlTotalTime . ' ' . A::t('core', 'sec') . '. | ' . A::t('core', 'total') . ': ' . (($result) ? $sth->rowCount() : '0 (error )') . ' ', $this->_query);
}
return $result;
@@ -436,7 +436,7 @@ public function delete($table, $where = '', $params = array())
$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);
+ CDebug::addMessage('queries', ''.++self::$count . '. delete | ' . $sqlTotalTime . ' ' . A::t('core', 'sec') . '. | ' . A::t('core', 'total') . ': ' . (($result) ? $result : '0 (warning )') . ' ', $this->_query);
}
return $result;
@@ -478,7 +478,7 @@ public function truncate($table)
$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);
+ CDebug::addMessage('queries', ''.++self::$count . '. truncate | ' . $sqlTotalTime . ' ' . A::t('core', 'sec') . '. | ' . A::t('core', 'total') . ': ' . (($result) ? $result : '0 (warning )') . ' ', $this->_query);
}
return $result;
@@ -545,7 +545,7 @@ public function customQuery($sql, $params = array(), $fetchMode = PDO::FETCH_ASS
$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);
+ CDebug::addMessage('queries', ''.++self::$count . '. query | ' . $sqlTotalTime . ' ' . A::t('core', 'sec') . '. | ' . A::t('core', 'total') . ': ' . (($result) ? count($result) : '0 (error )') . ' ', $this->_query);
}
return $result;
@@ -594,7 +594,7 @@ public function customExec($sql, $params = array(), $forceUpdate = false)
$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);
+ CDebug::addMessage('queries', ''.++self::$count . '. query | ' . $sqlTotalTime . ' ' . A::t('core', 'sec') . '. | ' . A::t('core', 'total') . ': ' . (($result) ? $result : '0 (error )') . ' ', $this->_query);
}
return $result;
@@ -659,7 +659,7 @@ public function showTables()
$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);
+ CDebug::addMessage('queries', ''.++self::$count . '. query | ' . $sqlTotalTime . ' ' . A::t('core', 'sec') . '. | ' . A::t('core', 'total') . ': ' . (($result) ? count($result) : '0 (error )') . ' ', $this->_query);
}
return $result;
@@ -722,7 +722,7 @@ public function showColumns($table = '')
$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);
+ 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;
From dc0721a44917b74fb9d2daf35e80893b3be59c7e Mon Sep 17 00:00:00 2001
From: Samuel Akopyan <>
Date: Fri, 29 May 2020 12:14:58 +0300
Subject: [PATCH 17/45] Fixed curly braces in array to []
---
framework/vendors/tcpdf/include/tcpdf_filters.php | 4 ++--
framework/vendors/tcpdf/include/tcpdf_images.php | 2 +-
framework/vendors/tcpdf/tcpdf_parser.php | 4 ++--
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/framework/vendors/tcpdf/include/tcpdf_filters.php b/framework/vendors/tcpdf/include/tcpdf_filters.php
index dfb80c5..63c9316 100644
--- a/framework/vendors/tcpdf/include/tcpdf_filters.php
+++ b/framework/vendors/tcpdf/include/tcpdf_filters.php
@@ -279,7 +279,7 @@ public static function decodeFilterLZWDecode($data) {
// convert string to binary string
$bitstring = '';
for ($i = 0; $i < $data_length; ++$i) {
- $bitstring .= sprintf('%08b', ord($data{$i}));
+ $bitstring .= sprintf('%08b', ord($data[$i]));
}
// get the number of bits
$data_length = strlen($bitstring);
@@ -376,7 +376,7 @@ public static function decodeFilterRunLengthDecode($data) {
$i = 0;
while($i < $data_length) {
// get current byte value
- $byte = ord($data{$i});
+ $byte = ord($data[$i]);
if ($byte == 128) {
// a length value of 128 denote EOD
break;
diff --git a/framework/vendors/tcpdf/include/tcpdf_images.php b/framework/vendors/tcpdf/include/tcpdf_images.php
index c2e3c36..018f243 100644
--- a/framework/vendors/tcpdf/include/tcpdf_images.php
+++ b/framework/vendors/tcpdf/include/tcpdf_images.php
@@ -315,7 +315,7 @@ public static function _parsepng($file) {
if ($n > 0) {
$trns = array();
for ($i = 0; $i < $n; ++ $i) {
- $trns[] = ord($t{$i});
+ $trns[] = ord($t[$i]);
}
}
}
diff --git a/framework/vendors/tcpdf/tcpdf_parser.php b/framework/vendors/tcpdf/tcpdf_parser.php
index 780ec21..05a0588 100644
--- a/framework/vendors/tcpdf/tcpdf_parser.php
+++ b/framework/vendors/tcpdf/tcpdf_parser.php
@@ -531,10 +531,10 @@ protected function getRawObject($offset=0) {
if ($char == '(') {
$open_bracket = 1;
while ($open_bracket > 0) {
- if (!isset($this->pdfdata{$strpos})) {
+ if (!isset($this->pdfdata[$strpos])) {
break;
}
- $ch = $this->pdfdata{$strpos};
+ $ch = $this->pdfdata[$strpos];
switch ($ch) {
case '\\': { // REVERSE SOLIDUS (5Ch) (Backslash)
// skip next character
From 3f47e275e8c0a23a5d03808703aae77262e06def Mon Sep 17 00:00:00 2001
From: Samuel Akopyan <>
Date: Sat, 30 May 2020 02:20:31 +0300
Subject: [PATCH 18/45] Fixed curly braces in array to []
---
framework/helpers/CImage.php | 4 ++++
framework/helpers/widgets/CDataForm.php | 2 +-
framework/helpers/widgets/CFormValidation.php | 2 +-
framework/helpers/widgets/CFormView.php | 2 +-
4 files changed, 7 insertions(+), 3 deletions(-)
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/widgets/CDataForm.php b/framework/helpers/widgets/CDataForm.php
index d5a4a17..463c82a 100644
--- a/framework/helpers/widgets/CDataForm.php
+++ b/framework/helpers/widgets/CDataForm.php
@@ -354,7 +354,7 @@ public static function init($params = array())
} elseif ($fieldType == 'image') {
unset($recordsAssoc[$field]);
} elseif ($fieldType == 'data') {
- $fieldValue = self::keyAt('default', $fieldInfo, '');
+ $fieldValue = self::keyAt('default', $fieldInfo, null);
} elseif ($fieldType == 'imageupload') {
if (!empty($_FILES[$field]['name'])) {
$targetPath = self::keyAt('validation.targetPath', $fieldInfo, '');
diff --git a/framework/helpers/widgets/CFormValidation.php b/framework/helpers/widgets/CFormValidation.php
index a53afc7..8c98ea4 100644
--- a/framework/helpers/widgets/CFormValidation.php
+++ b/framework/helpers/widgets/CFormValidation.php
@@ -520,7 +520,7 @@ private static function _validateMaxLength($fieldValue, $maxLength, $title, $msg
}
}
} elseif (!CValidator::validateMaxLength($fieldValue, $maxLength)) {
- self::$_errorMessage = A::t($msgSource, 'The {title} field length may be {max_length} characters maximum! Please re-enter.', array('{title}' => $title, '{max_length}' => $maxLength));
+ self::$_errorMessage = A::t($msgSource, 'The {title} field length may be {max_length} characters maximum! Please re-enter.', array('{title}' => $title, '{max_length}' => number_format($maxLength)));
$result = false;
}
diff --git a/framework/helpers/widgets/CFormView.php b/framework/helpers/widgets/CFormView.php
index 780fa7b..c9aac59 100644
--- a/framework/helpers/widgets/CFormView.php
+++ b/framework/helpers/widgets/CFormView.php
@@ -725,7 +725,7 @@ private static function _formField($field, $fieldInfo, $events, $formName = '',
case 'textarea':
$maxLength = (int)self::keyAt('maxLength', $htmlOptions, 0);
- if ($maxLength > 0) $appendLabel = ' ' . A::t('core', 'max.: {maxchars} chars', array('{maxchars}' => $maxLength));
+ if ($maxLength > 0) $appendLabel = ' ' . A::t('core', 'max.: {maxchars} chars', array('{maxchars}' => number_format($maxLength)));
$fieldHtml = CHtml::textArea($field, $value, $htmlOptions);
break;
From f08fc4429e36d5f2b0a9a70c1815a5e8ad90a98b Mon Sep 17 00:00:00 2001
From: Samuel Akopyan <>
Date: Tue, 2 Jun 2020 22:37:22 +0300
Subject: [PATCH 19/45] Added __isset to some classes
---
framework/db/CActiveRecord.php | 29 ++++++++++++++++++++---------
framework/db/CRecordEntity.php | 17 ++++++++++++++---
2 files changed, 34 insertions(+), 12 deletions(-)
diff --git a/framework/db/CActiveRecord.php b/framework/db/CActiveRecord.php
index eaab3b9..105c919 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
@@ -191,7 +192,17 @@ public function __get($index)
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
diff --git a/framework/db/CRecordEntity.php b/framework/db/CRecordEntity.php
index 6d58427..f2a1c15 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
@@ -79,9 +80,19 @@ public function __get($index)
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 active record property to be null
+ * Sets a record entity property to be null
* @param string $index
* @return void
*/
From 61d2f3cf6b8c539088205da3b5eaefbcd014c2e0 Mon Sep 17 00:00:00 2001
From: Samuel Akopyan <>
Date: Tue, 7 Jul 2020 22:10:32 +0300
Subject: [PATCH 20/45] Fix for PHP5-7 syntac nusoap classes
---
framework/vendors/nusoap/nusoap.php | 482 ++++++++++++++--------------
1 file changed, 233 insertions(+), 249 deletions(-)
diff --git a/framework/vendors/nusoap/nusoap.php b/framework/vendors/nusoap/nusoap.php
index bc061a0..d18f2cb 100644
--- a/framework/vendors/nusoap/nusoap.php
+++ b/framework/vendors/nusoap/nusoap.php
@@ -49,6 +49,11 @@
* RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies
* RFC 2068 Hypertext Transfer Protocol -- HTTP/1.1
* RFC 2617 HTTP Authentication: Basic and Digest Access Authentication
+ *
+ * FIXES by ApPHP:
+ * --------
+ * 07/07/2020 - class declaration changed to PHP5-7
+ * 07/07/2020 - undefined var fix
*/
/* load classes
@@ -90,35 +95,35 @@ class nusoap_base {
* @var string
* @access private
*/
- var $title = 'NuSOAP';
+ public $title = 'NuSOAP';
/**
* Version for HTTP headers.
*
* @var string
* @access private
*/
- var $version = '0.9.5';
+ public $version = '0.9.5';
/**
* CVS revision for HTTP headers.
*
* @var string
* @access private
*/
- var $revision = '$Revision: 1.123 $';
+ public $revision = '$Revision: 1.123 $';
/**
* Current error string (manipulated by getError/setError)
*
* @var string
* @access private
*/
- var $error_str = '';
+ public $error_str = '';
/**
* Current debug string (manipulated by debug/appendDebug/clearDebug/getDebug/getDebugAsXMLComment)
*
* @var string
* @access private
*/
- var $debug_str = '';
+ public $debug_str = '';
/**
* toggles automatic encoding of special characters as entities
* (should always be true, I think)
@@ -126,14 +131,14 @@ class nusoap_base {
* @var boolean
* @access private
*/
- var $charencoding = true;
+ public $charencoding = true;
/**
* the debug level for this instance
*
* @var integer
* @access private
*/
- var $debugLevel;
+ public $debugLevel;
/**
* set schema version
@@ -141,7 +146,7 @@ class nusoap_base {
* @var string
* @access public
*/
- var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
+ public $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
/**
* charset encoding for outgoing messages
@@ -149,8 +154,8 @@ class nusoap_base {
* @var string
* @access public
*/
- var $soap_defencoding = 'ISO-8859-1';
- //var $soap_defencoding = 'UTF-8';
+ public $soap_defencoding = 'ISO-8859-1';
+ //public $soap_defencoding = 'UTF-8';
/**
* namespaces in an array of prefix => uri
@@ -160,7 +165,7 @@ class nusoap_base {
* @var array
* @access public
*/
- var $namespaces = array(
+ public $namespaces = array(
'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
'xsd' => 'http://www.w3.org/2001/XMLSchema',
'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
@@ -173,7 +178,7 @@ class nusoap_base {
* @var array
* @access private
*/
- var $usedNamespaces = array();
+ public $usedNamespaces = array();
/**
* XML Schema types in an array of uri => (array of xml type => php type)
@@ -182,7 +187,7 @@ class nusoap_base {
* @var array
* @access public
*/
- var $typemap = array(
+ public $typemap = array(
'http://www.w3.org/2001/XMLSchema' => array(
'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double',
'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'',
@@ -215,7 +220,7 @@ class nusoap_base {
* @deprecated
* @see expandEntities
*/
- var $xmlEntities = array('quot' => '"','amp' => '&',
+ public $xmlEntities = array('quot' => '"','amp' => '&',
'lt' => '<','gt' => '>','apos' => "'");
/**
@@ -223,7 +228,7 @@ class nusoap_base {
*
* @access public
*/
- function nusoap_base() {
+ function __construct() {
$this->debugLevel = $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'];
}
@@ -993,9 +998,6 @@ function usleepWindows($usec)
while ($timePassed < $usec);
}
-?>faultcode = $faultcode;
$this->faultactor = $faultactor;
@@ -1080,9 +1082,6 @@ function serialize(){
class soap_fault extends nusoap_fault {
}
-?>debug('nusoap_xmlschema class instantiated, inside constructor');
// files
@@ -1878,27 +1877,28 @@ function getTypeDef($type){
*/
function serializeTypeDef($type){
//print "in sTD() for type $type ";
- if($typeDef = $this->getTypeDef($type)){
- $str .= '<'.$type;
- if(is_array($typeDef['attrs'])){
- foreach($typeDef['attrs'] as $attName => $data){
- $str .= " $attName=\"{type = ".$data['type']."}\"";
- }
- }
- $str .= " xmlns=\"".$this->schema['targetNamespace']."\"";
- if(count($typeDef['elements']) > 0){
- $str .= ">";
- foreach($typeDef['elements'] as $element => $eData){
- $str .= $this->serializeTypeDef($element);
- }
- $str .= "$type>";
- } elseif($typeDef['typeClass'] == 'element') {
- $str .= ">$type>";
- } else {
- $str .= "/>";
- }
- return $str;
- }
+ if($typeDef = $this->getTypeDef($type)){
+ $str = '<'.$type;
+ if (is_array($typeDef['attrs'])) {
+ foreach ($typeDef['attrs'] as $attName => $data) {
+ $str .= " $attName=\"{type = ".$data['type']."}\"";
+ }
+ }
+ $str .= " xmlns=\"".$this->schema['targetNamespace']."\"";
+ if (count($typeDef['elements']) > 0) {
+ $str .= ">";
+ foreach ($typeDef['elements'] as $element => $eData) {
+ $str .= $this->serializeTypeDef($element);
+ }
+ $str .= "$type>";
+ } elseif ($typeDef['typeClass'] == 'element') {
+ $str .= ">$type>";
+ } else {
+ $str .= "/>";
+ }
+
+ return $str;
+ }
return false;
}
@@ -1913,6 +1913,7 @@ function serializeTypeDef($type){
* @deprecated
*/
function typeToForm($name,$type){
+ $buffer = '';
// get typedef
if($typeDef = $this->getTypeDef($type)){
// if struct
@@ -2050,9 +2051,6 @@ function addElement($attrs) {
class XMLSchema extends nusoap_xmlschema {
}
-?>name = $name;
$this->type = $type;
@@ -2154,10 +2152,6 @@ function decode(){
-?>debug("ctor url=$url use_curl=$use_curl curl_options:");
$this->appendDebug($this->varDump($curl_options));
@@ -3458,9 +3452,6 @@ function getCookiesForRequest($cookies, $secure=false) {
}
}
-?> opData; operations are added by the register()
@@ -3601,25 +3592,25 @@ class nusoap_server extends nusoap_base {
* @var array
* @access private
*/
- var $operations = array();
+ public $operations = array();
/**
* wsdl instance (if one)
* @var mixed
* @access private
*/
- var $wsdl = false;
+ public $wsdl = false;
/**
* URL for WSDL (if one)
* @var mixed
* @access private
*/
- var $externalWSDLURL = false;
+ public $externalWSDLURL = false;
/**
* whether to append debug to response as XML comment
* @var boolean
* @access public
*/
- var $debug_flag = false;
+ public $debug_flag = false;
/**
@@ -3629,7 +3620,7 @@ class nusoap_server extends nusoap_base {
* @param mixed $wsdl file path or URL (string), or wsdl instance (object)
* @access public
*/
- function nusoap_server($wsdl=false){
+ function __construct($wsdl=false){
parent::nusoap_base();
// turn on debugging?
global $debug;
@@ -4582,9 +4573,6 @@ function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style=
class soap_server extends nusoap_server {
}
-?>debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
$this->proxyhost = $proxyhost;
@@ -6517,8 +6505,6 @@ function addOperation($name, $in = false, $out = false, $namespace = false, $soa
return true;
}
}
-?> pos
- var $ids = array();
+ public $ids = array();
// array of id => hrefs => pos
- var $multirefs = array();
+ public $multirefs = array();
// toggle for auto-decoding element content
- var $decode_utf8 = true;
+ public $decode_utf8 = true;
/**
* constructor that actually does the parsing
@@ -6575,7 +6561,7 @@ class nusoap_parser extends nusoap_base {
* @param string $decode_utf8 whether to decode UTF-8 to ISO-8859-1
* @access public
*/
- function nusoap_parser($xml,$encoding='UTF-8',$method='',$decode_utf8=true){
+ function __construct($xml,$encoding='UTF-8',$method='',$decode_utf8=true){
parent::nusoap_base();
$this->xml = $xml;
$this->xml_encoding = $encoding;
@@ -7049,6 +7035,7 @@ function buildVal($pos){
}
$this->debug('in buildVal() for '.$this->message[$pos]['name']."(pos $pos) of type ".$this->message[$pos]['type']);
// if there are children...
+ $params = [];
if($this->message[$pos]['children'] != ''){
$this->debug('in buildVal, there are children');
$children = explode('|',$this->message[$pos]['children']);
@@ -7157,9 +7144,6 @@ function buildVal($pos){
class soap_parser extends nusoap_parser {
}
-?>endpoint = $endpoint;
$this->proxyhost = $proxyhost;
From ba69464c219bb88264ba5add53644b7cff0c9a32 Mon Sep 17 00:00:00 2001
From: Samuel Akopyan <>
Date: Tue, 7 Jul 2020 22:11:29 +0300
Subject: [PATCH 21/45] Fix for PHP5-7 syntac tmhOAuth classe
---
.../opauth/Strategy/Twitter/Vendor/tmhOAuth/tmhOAuth.php | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/framework/vendors/opauth/Strategy/Twitter/Vendor/tmhOAuth/tmhOAuth.php b/framework/vendors/opauth/Strategy/Twitter/Vendor/tmhOAuth/tmhOAuth.php
index 01e51df..a7b9598 100644
--- a/framework/vendors/opauth/Strategy/Twitter/Vendor/tmhOAuth/tmhOAuth.php
+++ b/framework/vendors/opauth/Strategy/Twitter/Vendor/tmhOAuth/tmhOAuth.php
@@ -10,11 +10,16 @@
* @version 0.7.1
*
* 27 October 2012
+ *
+ * FIXES by ApPHP:
+ * --------
+ * 07/07/2020 - class declaration changed to PHP5-7
+ *
*/
class tmhOAuth {
const VERSION = '0.7.1';
- var $response = array();
+ public $response = array();
/**
* Creates a new tmhOAuth object
From 20fbe10cfe9835cb2012b9216217ea92448d1fbe Mon Sep 17 00:00:00 2001
From: Samuel Akopyan <>
Date: Sat, 25 Jul 2020 17:39:32 +0300
Subject: [PATCH 22/45] Chunk
---
demos/simple-blog/protected/config/db.php | 12 ++
demos/simple-blog/protected/config/main.php | 193 ++++++++++++++++++
.../protected/controllers/PostsController.php | 15 +-
3 files changed, 218 insertions(+), 2 deletions(-)
create mode 100644 demos/simple-blog/protected/config/db.php
create mode 100644 demos/simple-blog/protected/config/main.php
diff --git a/demos/simple-blog/protected/config/db.php b/demos/simple-blog/protected/config/db.php
new file mode 100644
index 0000000..f0681cd
--- /dev/null
+++ b/demos/simple-blog/protected/config/db.php
@@ -0,0 +1,12 @@
+ array(
+ 'driver' => 'mysql',
+ 'host' => 'localhost',
+ 'database' => 'test',
+ 'username' => 'root',
+ 'password' => '',
+ 'prefix' => 'mb_',
+ )
+);
\ No newline at end of file
diff --git a/demos/simple-blog/protected/config/main.php b/demos/simple-blog/protected/config/main.php
new file mode 100644
index 0000000..7af26d2
--- /dev/null
+++ b/demos/simple-blog/protected/config/main.php
@@ -0,0 +1,193 @@
+ 'Simple Blog',
+ 'version' => '1.0.2',
+
+ // Directy CMF data
+ 'directy_cmf_version' => '',
+
+ // installation settings
+ 'installationKey' => '2x4686fpyk',
+
+ // Password keys settings (for database passwords only)
+ // Remember: changing these settings after installation may lead to unstable work of application
+ // encryptAlgorithm - md5, sha1 (not recommended), sha256, whirlpool, etc
+ 'password' => array(
+ 'encryption' => true,
+ 'encryptAlgorithm' => 'sha256',
+ 'encryptSalt' => true,
+ 'hashKey' => 'apphp_directy_login_system',
+ ),
+
+ // Text encryption settings (for database text fields only)
+ // Remember: changing these settings after installation may lead to unstable work of application
+ // Encryption level - PHP or DB
+ // encryptAlgorithm - PHP: aes-256-cbc DB: AES
+ 'text' => array(
+ 'encryption' => true,
+ 'encryptAlgorithm' => 'aes-256-cbc',
+ 'encryptKey' => 'apphp_directy_cmf',
+ ),
+
+ // Default email settings
+ 'email' => array(
+ 'mailer' => 'smtpMailer', /* phpMail | phpMailer | smtpMailer */
+ 'from' => 'info@email.me',
+ 'fromName' => '', /* John Smith */
+ 'isHtml' => true,
+ 'smtp' => array(
+ 'auth' => true, /* true or false */
+ 'secure' => 'ssl', /* 'ssl', 'tls' or '' */
+ 'host' => 'smtp.gmail.com',
+ 'port' => '465',
+ 'username' => '',
+ 'password' => '',
+ ),
+ ),
+
+ // Validations
+ // Define array of 'excluded' controllers, ex.: array('PaymentProviders', 'Checkout')
+ // Token type: 'session', 'cookie' or 'multipages'
+ 'validation' => array(
+ 'csrf' => array('enable' => false, 'exclude' => array('PaymentProviders'), 'tokenType' => 'session'),
+ 'bruteforce' => array('enable' => true, 'badLogins' => 5, 'badRestores' => 5, 'redirectDelay' => 3)
+ ),
+
+ // Exception handling
+ // Define exceptions exceptions in application
+ 'exceptionHandling' => array(
+ 'enable' => true,
+ 'level' => 'global'
+ ),
+
+ // Output compression
+ 'compression' => array(
+ 'gzip' => array('enable' => false),
+ 'html' => array('enable' => false),
+ 'css' => array('enable' => false, 'path' => 'assets/minified/css/', 'minify' => true),
+ 'js' => array('enable' => false, 'path' => 'assets/minified/js/', 'minify' => true),
+ ),
+
+ // Session settings
+ 'session' => array(
+ 'customStorage' => false, /* true value means use a custom storage (database), false - standard storage */
+ 'cacheLimiter' => '', /* to prevent 'Web Page expired' message for POST request use "private,must-revalidate" */
+ 'lifetime' => 24, /* session timeout in minutes, default: 24 min = 1440 sec */
+ ),
+
+ // Cookies settings
+ 'cookies' => array(
+ 'domain' => '',
+ 'path' => '/'
+ ),
+
+ // Cache settings
+ 'cache' => array(
+ 'enable' => false,
+ 'type' => 'auto', /* 'auto' or 'manual' */
+ 'lifetime' => 20, /* in minutes */
+ 'path' => 'protected/tmp/cache/'
+ ),
+
+ // Logger settings
+ 'log' => array(
+ 'enable' => false,
+ 'path' => 'protected/tmp/logs/',
+ 'fileExtension' => 'php',
+ 'dateFormat' => 'Y-m-d H:i:s',
+ 'threshold' => 1,
+ 'filePermissions' => 0644,
+ 'lifetime' => 30 /* in days */
+ ),
+
+ // RSS Feed settings
+ 'rss' => array(
+ 'path' => 'feeds/'
+ ),
+
+ // Datetime settings
+ 'defaultTimeZone' => 'UTC',
+
+ // Template default settings
+ 'template' => array(
+ 'default' => 'default'
+ ),
+
+ // Layout default settings
+ 'layouts' => array(
+ 'enable' => false,
+ 'default' => 'default'
+ ),
+
+ // Layout default settings
+ 'layouts' => array(
+ 'enable' => array('frontend' => false, 'backend' => false),
+ 'default' => 'default'
+ ),
+
+ // Application default settings
+ 'defaultBackendDirectory' => 'backoffice', /* default backoffice directory */
+ 'defaultErrorController' => 'Error', /* may be overridden by module settings */
+ 'defaultController' => 'Index', /* may be overridden by module settings */
+ 'defaultAction' => 'index', /* may be overridden by module settings */
+
+ // Application Backend settings
+ 'restoreAdminPassword' => array(
+ 'enable' => true,
+ 'recoveryType' => 'direct' /* 'direct' - send new password directly, 'recovery' - send link to recovery page */
+ ),
+
+ // Application components
+ 'components' => array(
+ 'Bootstrap' => array('enable' => true, 'class' => 'Bootstrap'),
+ 'BlogMenu' => array('enable' => true, 'class' => 'BlogMenu'),
+ ),
+
+ // Logger settings
+ 'log' => array(
+ 'enable' => false,
+ 'path' => 'protected/tmp/logs/',
+ 'fileExtension' => 'php',
+ 'dateFormat' => 'Y-m-d H:i:s',
+ 'threshold' => 1,
+ 'filePermissions' => 0644,
+ 'lifetime' => 30 /* in days */
+ ),
+
+ // Widget settings
+ 'widgets' => array(
+ 'paramKeysSensitive' => true
+ ),
+
+ // Application helpers
+ 'helpers' => array(
+ //'helper' => array('enable' => true, 'class' => 'Helper'),
+ ),
+
+ // Application modules
+ 'modules' => array(
+ 'setup' => array('enable' => true, 'removable' => false, 'backendDefaultUrl' => ''),
+ ),
+
+ // Url manager
+ 'urlManager' => array(
+ 'urlFormat' => 'shortPath', /* get | path | shortPath */
+ 'rules' => array(
+ // Required by payments module. If you remove these rules - make sure you define full path URL for pyment providers
+ //'paymentProviders/handlePayment/provider/([a-zA-Z0-9\_]+)/handler/([a-zA-Z0-9\_]+)/module/([a-zA-Z0-9\_]+)[\/]?$' => 'paymentProviders/handlePayment/provider/{$0}/handler/{$1}/module/{$2}',
+ 'paymentProviders/handlePayment/([a-zA-Z0-9\_]+)/([a-zA-Z0-9\_]+)/([a-zA-Z0-9\_]+)[\/]?$' => 'paymentProviders/handlePayment/provider/{$0}/handler/{$1}/module/{$2}',
+ //'paymentProviders/handlePayment/provider/([a-zA-Z0-9\_]+)/handler/([a-zA-Z0-9\_]+)[\/]?$' => 'paymentProviders/handlePayment/provider/{$0}/handler/{$1}',
+ 'paymentProviders/handlePayment/([a-zA-Z0-9\_]+)/([a-zA-Z0-9\_]+)[\/]?$' => 'paymentProviders/handlePayment/provider/{$0}/handler/{$1}',
+ //'paymentProviders/handlePayment/provider/([a-zA-Z0-9\_]+)[\/]?$' => 'paymentProviders/handlePayment/provider/{$0}',
+ 'paymentProviders/handlePayment/([a-zA-Z0-9\_]+)[\/]?$' => 'paymentProviders/handlePayment/provider/{$0}',
+ // Required by dynamic pages, if you want to use user-friendly URLs
+ //'controller/action/value1/value2' => 'controllerName/action/param1/value1/param2/value2',
+ //'sitepages/show/example-page-1' => 'sitePages/show/name/about-us',
+ //'value1' => 'controllerName/action/param1/value1',
+ //'about-us' => 'sitePages/show/name/about-us',
+ ),
+ ),
+
+);
diff --git a/demos/simple-blog/protected/controllers/PostsController.php b/demos/simple-blog/protected/controllers/PostsController.php
index 3b58c0f..cb363f8 100644
--- a/demos/simple-blog/protected/controllers/PostsController.php
+++ b/demos/simple-blog/protected/controllers/PostsController.php
@@ -135,10 +135,21 @@ public function indexAction($msg = '')
if (!$this->_view->currentPage) {
$this->_view->actionMessage = CWidget::create('CMessage', array('error', 'Wrong parameter passed! Please try again later.', array('button' => true)));
} else {
- $this->_view->posts = Posts::model()->findAll(array(
+/* $this->_view->posts = Posts::model()->findAll(array(
'limit' => (($this->_view->currentPage - 1) * $this->_view->pageSize) . ', ' . $this->_view->pageSize,
'order' => 'post_datetime DESC',
- ));
+ ));*/
+
+ $posts = null;
+ Posts::model()->chunk(2, function ($records) use(&$posts){
+ foreach ($records as $key => $record) {
+ $posts[] = $record;
+ }
+ //CDebug::d($record);
+ });
+ //CDebug::dd($posts);
+
+ $this->_view->posts = $posts;
}
$this->_view->render('posts/index');
From a28fc02b504bb277ab3f686de24ffac76a4a5366 Mon Sep 17 00:00:00 2001
From: Samuel Akopyan <>
Date: Sat, 25 Jul 2020 18:16:03 +0300
Subject: [PATCH 23/45] Added possibility to close debug panel to minimum size
---
CHANGELOG | 1 +
framework/core/CDebug.php | 16 ++++++++++++++--
framework/db/CActiveRecord.php | 27 +++++++++++++++++++++++++++
3 files changed, 42 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG b/CHANGELOG
index f08bb95..97820c3 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
Version 1.4.x -
----------------------------
- Enh: added possibility to hide system queries in debug panel
+- Enh: added possibility to close debug panel to minimum size
- Bug: fixed wrong assignment of _isRendered in CView
diff --git a/framework/core/CDebug.php b/framework/core/CDebug.php
index 8d2ffe7..5f47cbf 100644
--- a/framework/core/CDebug.php
+++ b/framework/core/CDebug.php
@@ -416,10 +416,12 @@ function appGetCookie(name){ if(document.cookie.length > 0){ start_c = document.
function appTabsMiddle(){ appExpandTabs("middle", appGetCookie("debugBarTab")); }
function appTabsMaximize(){ appExpandTabs("max", appGetCookie("debugBarTab")); }
function appTabsMinimize(){ appExpandTabs("min", "General"); }
+ function appTabsClose(){ appExpandTabs("close", appGetCookie("debugBarTab")); }
function appExpandTabs(act, key){
if(act == "max"){ debugTabsHeight = "500px"; }
else if(act == "middle"){ debugTabsHeight = "200px"; }
else if(act == "min"){ debugTabsHeight = "0px"; }
+ else if(act == "close"){ debugTabsHeight = "0px"; }
else if(act == "auto"){
if(debugTabsHeight == "0px"){ debugTabsHeight = "200px"; act = "middle"; }
else if(debugTabsHeight == "200px"){ act = "middle"; }
@@ -437,6 +439,14 @@ function appExpandTabs(act, key){
}
}
if(act != "min"){
+ x = document.getElementsByClassName("item");
+ if(act == "close"){
+ for (i = 0; i < x.length; i++) x[i].style.display = "none";
+ document.getElementsByClassName("narrow-close")[0].style.display = "none";
+ }else{
+ for (i = 0; i < x.length; i++) x[i].style.display = "";
+ document.getElementsByClassName("narrow-close")[0].style.display = "";
+ }
document.getElementById("content"+keyTab).style.display = "";
document.getElementById("content"+keyTab).style.cssText = "width:100%;height:"+debugTabsHeight+";overflow-y:auto;";
if(document.getElementById("tab"+keyTab).className == "tab-orange"){
@@ -458,7 +468,7 @@ function appExpandTabs(act, key){
-
+
' . A::t('core', 'Debug') . ':
@@ -474,7 +484,7 @@ function appExpandTabs(act, key){
▼
☐
▣
- ×
+ ×
@@ -685,6 +695,8 @@ function appExpandTabs(act, key){
$output .= '';
} elseif ($debugBarState == 'middle') {
$output .= '';
+ } elseif ($debugBarState == 'close') {
+ $output .= '';
} else {
$output .= '';
}
diff --git a/framework/db/CActiveRecord.php b/framework/db/CActiveRecord.php
index 105c919..57839ac 100644
--- a/framework/db/CActiveRecord.php
+++ b/framework/db/CActiveRecord.php
@@ -43,6 +43,8 @@
* getTranslations
* saveTranslations
*
+ * chunk
+ *
* find
* findByPk
* findByAttributes
@@ -576,6 +578,31 @@ private function _createObjectFromTable()
return true;
}
+
+ /**
+ * Split AR result into parts (chunks)
+ * @param int $size
+ * @param null $callback
+ */
+ public function chunk(int $size, callable $callback = null)
+ {
+ if (is_int($size) && $size > 0 && !empty($callback)) {
+ $from = 0;
+// echo('limit'."$from, $size");
+ while ($result = $this->findAll(array('limit'=>"$from, $size"))){
+ $callback($result);
+ $from += $size;
+// echo('limit'."$from, $size");
+// if ($from > 10){
+// return;
+// }
+ }
+ } else {
+ CDebug::AddMessage('errors', 'chunk', A::t('core', 'Wrong params for chunk: {size} or callback method is callable.', array('{size}' => $size)));
+ }
+
+ return null;
+ }
/**
* This method queries your database to find first related object
From e76f979c7a4a1b19e63bfca2f921f9647507af80 Mon Sep 17 00:00:00 2001
From: Samuel Akopyan <>
Date: Sat, 25 Jul 2020 18:30:38 +0300
Subject: [PATCH 24/45] Added padding to command line commands output
---
framework/console/CConsole.php | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/framework/console/CConsole.php b/framework/console/CConsole.php
index 4d6961b..bef0ea2 100644
--- a/framework/console/CConsole.php
+++ b/framework/console/CConsole.php
@@ -73,13 +73,26 @@ public static function yellow($string = '')
/**
* Draw line with red background
+ *
* @param string $string
+ * @param bool $padding
+ *
* @return string
*/
- public static function redbg($string = '')
+ public static function redbg($string = '', $padding = true)
{
- return "\e[0;41m".$string."\e[0m";
- }
+ $length = strlen($string) + 4;
+ $output = '';
+
+ if ($padding) {
+ $output .= "\e[0;41m".str_pad(' ', $length, " ", STR_PAD_LEFT)."\e[0m" . PHP_EOL;
+ }
+ $output .= "\e[0;41m".($padding ? ' ' : '').$string.($padding ? ' ' : '')."\e[0m".PHP_EOL;
+ if ($padding) {
+ $output .= "\e[0;41m".str_pad(' ', $length, " ", STR_PAD_LEFT)."\e[0m" . PHP_EOL;
+ }
+ return $output;
+ }
}
\ No newline at end of file
From 60fcdd07d8c0a23c72b5f01222626122c7adf9b4 Mon Sep 17 00:00:00 2001
From: Samuel Akopyan <>
Date: Sat, 25 Jul 2020 19:00:52 +0300
Subject: [PATCH 25/45] Console command changes, implemented IConsoleCommand
interfaces
---
bin/aii.php | 4 +-
bin/protected/tmp/logs/error.log | 8 ---
framework/Apphp.php | 10 +--
framework/console/CCacheClearCommand.php | 71 +++++++++++++++++++-
framework/console/CConsole.php | 2 +
framework/console/CConsoleCommand.php | 43 ++----------
framework/console/CHelpCommand.php | 13 +++-
framework/console/CMakeControllerCommand.php | 34 ++++++++++
framework/console/CVersionCommand.php | 6 +-
framework/core/interfaces.php | 12 ++++
10 files changed, 147 insertions(+), 56 deletions(-)
delete mode 100644 bin/protected/tmp/logs/error.log
create mode 100644 framework/console/CMakeControllerCommand.php
diff --git a/bin/aii.php b/bin/aii.php
index 8e211a6..c7d402a 100644
--- a/bin/aii.php
+++ b/bin/aii.php
@@ -14,8 +14,8 @@
require_once($apphp);
A::init($config)->run();
-
-$console = new CConsole($argv);
+// We get automatically $argv and $argc, as we run command line command
+$console = new CConsole($argv);
$consoleCommand = new CConsoleCommand(
$console->getCommand(),
$console->getParams()
diff --git a/bin/protected/tmp/logs/error.log b/bin/protected/tmp/logs/error.log
deleted file mode 100644
index 98bf975..0000000
--- a/bin/protected/tmp/logs/error.log
+++ /dev/null
@@ -1,8 +0,0 @@
-[28-Mar-2020 13:28:18 UTC] PHP Fatal error: Uncaught Error: Class 'Modules\Setup\Controllers\SetupController' not found in C:\xampp\htdocs\git\php-mvc-framework\framework\core\CRouter.php:203
-Stack trace:
-#0 C:\xampp\htdocs\git\php-mvc-framework\framework\Apphp.php(548): CRouter->route()
-#1 C:\xampp\htdocs\git\php-mvc-framework\framework\Apphp.php(321): A->_runApp()
-#2 C:\xampp\htdocs\git\php-mvc-framework\bin\aii.php(15): A->run()
-#3 C:\xampp\htdocs\git\php-mvc-framework\bin\aii(15): require_once('C:\\xampp\\htdocs...')
-#4 {main}
- thrown in C:\xampp\htdocs\git\php-mvc-framework\framework\core\CRouter.php on line 203
diff --git a/framework/Apphp.php b/framework/Apphp.php
index 1f2e1a1..27e0a87 100644
--- a/framework/Apphp.php
+++ b/framework/Apphp.php
@@ -97,10 +97,12 @@ class A
);
/** @var array */
private static $_coreConsoleClasses = array(
- 'CConsole' => 'console/CConsole.php',
- 'CConsoleCommand' => 'console/CConsoleCommand.php',
- 'CHelpCommand' => 'console/CHelpCommand.php',
- 'CVersionCommand' => 'console/CVersionCommand.php',
+ 'CConsole' => 'console/CConsole.php',
+ 'CConsoleCommand' => 'console/CConsoleCommand.php',
+ 'CHelpCommand' => 'console/CHelpCommand.php',
+ 'CVersionCommand' => 'console/CVersionCommand.php',
+ 'CCacheClearCommand' => 'console/CCacheClearCommand.php',
+ 'CMakeControllerCommand' => 'console/CMakeControllerCommand.php',
);
/** @var array */
private static $_coreComponents = array(
diff --git a/framework/console/CCacheClearCommand.php b/framework/console/CCacheClearCommand.php
index b9e54f1..288f0ec 100644
--- a/framework/console/CCacheClearCommand.php
+++ b/framework/console/CCacheClearCommand.php
@@ -1,5 +1,72 @@
+ * @link http://www.apphpframework.com/
+ * @copyright Copyright (c) 2012 - 2020 ApPHP Framework
+ * @license http://www.apphpframework.com/license/
+ *
+ * PUBLIC (static): PROTECTED: PRIVATE (static):
+ * --------------- --------------- ---------------
+ * handle (static)
+ */
+
+class CCacheClearCommand implements IConsoleCommand
+{
+
+ /**
+ * Handle specific console command
+ * @param string $param
+ * @return string
+ */
+ public static function handle($param = '')
+ {
+ $output = '';
+
+ //var_dump($argv);
+ //var_dump($argc);
+
+ if (empty($param)) {
+ $output .= CConsole::redbg("No cache type for deleting is defined. Type cache:clear -help") . PHP_EOL;
+// } elseif ($param === '-h' || $param === '-help') {
+// $output .= CConsole::yellow("Usage:") . PHP_EOL;
+// $output .= " cache:clear-all\tFlush all application cache". PHP_EOL;
+// $output .= " cache:clear [type]\tFlush specific application cache". PHP_EOL;
+// $output .= " \t\t\t[type] - the type of cache to be removed: 'db', 'css', 'js' or 'all'". PHP_EOL;
+// } elseif (in_array($param, array('db', 'css', 'js', 'all'))) {
+// if($param == 'db' || $param == 'all'){
+// if (CConfig::get('cache.db.path') == '') {
+// $output .= CConsole::redbg("Config value 'cache.db.path' is not defined. Check your configuration file.") . PHP_EOL;
+// }else{
+// $result = CFile::emptyDirectory(CConfig::get('cache.db.path'), array('index.html'));
+// $output .= 'DB cache ' . ($result ? 'successfully cleaned' : 'error');
+// }
+// }
+// if($param == 'css' || $param == 'all'){
+// if (CConfig::get('compression.css.path') == '') {
+// $output .= CConsole::redbg("Config value 'compression.css.path' is not defined. Check your configuration file.") . PHP_EOL;
+// }else{
+// $result = CFile::emptyDirectory(CConfig::get('compression.css.path'), array('index.html'));
+// $output .= 'CSS cache ' . ($result ? 'successfully cleaned' : 'error');
+// }
+// }
+// if($param == 'js' || $param == 'all'){
+// if (CConfig::get('compression.js.path') == '') {
+// $output .= CConsole::redbg("Config value 'compression.js.path' is not defined. Check your configuration file.") . PHP_EOL;
+// }else{
+// $result = CFile::emptyDirectory(CConfig::get('compression.js.path'), array('index.html'));
+// $output .= 'JS cache ' . ($result ? 'successfully cleaned' : 'error');
+// }
+// }
+ }
+
+
+ return $output;
+ }
+
+}
+
diff --git a/framework/console/CConsole.php b/framework/console/CConsole.php
index bef0ea2..46c544d 100644
--- a/framework/console/CConsole.php
+++ b/framework/console/CConsole.php
@@ -27,6 +27,8 @@ class CConsole
/**
* Class constructor
+ *
+ * @param array $argv
*/
public function __construct($argv = array())
{
diff --git a/framework/console/CConsoleCommand.php b/framework/console/CConsoleCommand.php
index 99f7168..9e10aed 100644
--- a/framework/console/CConsoleCommand.php
+++ b/framework/console/CConsoleCommand.php
@@ -46,64 +46,31 @@ public function run($return = false)
switch ($this->command) {
- case '':
case '-h':
case '--help':
-
+ case 'help':
$output .= CHelpCommand::handle();
break;
case '-v':
case '--version':
-
$output .= CVersionCommand::handle();
break;
case 'cache:clear':
case 'cache:clear-all':
-
if ($this->command === 'cache:clear-all') {
$this->param = 'all';
}
- if (empty($this->param)) {
- $output .= CConsole::redbg("No cache type for deleting is defined. Type cache:clear -help") . PHP_EOL;
- } elseif ($this->param === '-h' || $this->param === '-help') {
- $output .= CConsole::yellow("Usage:") . PHP_EOL;
- $output .= " cache:clear-all\tFlush all application cache". PHP_EOL;
- $output .= " cache:clear [type]\tFlush specific application cache". PHP_EOL;
- $output .= " \t\t\t[type] - the type of cache to be removed: 'db', 'css', 'js' or 'all'". PHP_EOL;
- } elseif (in_array($this->param, array('db', 'css', 'js', 'all'))) {
- if($this->param == 'db' || $this->param == 'all'){
- if (CConfig::get('cache.db.path') == '') {
- $output .= CConsole::redbg("Config value 'cache.db.path' is not defined. Check your configuration file.") . PHP_EOL;
- }else{
- $result = CFile::emptyDirectory(CConfig::get('cache.db.path'), array('index.html'));
- $output .= 'DB cache ' . ($result ? 'successfully cleaned' : 'error');
- }
- }
- if($this->param == 'css' || $this->param == 'all'){
- if (CConfig::get('compression.css.path') == '') {
- $output .= CConsole::redbg("Config value 'compression.css.path' is not defined. Check your configuration file.") . PHP_EOL;
- }else{
- $result = CFile::emptyDirectory(CConfig::get('compression.css.path'), array('index.html'));
- $output .= 'CSS cache ' . ($result ? 'successfully cleaned' : 'error');
- }
- }
- if($this->param == 'js' || $this->param == 'all'){
- if (CConfig::get('compression.js.path') == '') {
- $output .= CConsole::redbg("Config value 'compression.js.path' is not defined. Check your configuration file.") . PHP_EOL;
- }else{
- $result = CFile::emptyDirectory(CConfig::get('compression.js.path'), array('index.html'));
- $output .= 'JS cache ' . ($result ? 'successfully cleaned' : 'error');
- }
- }
- }
+ $output .= CCacheClearCommand::handle($this->param);
+ break;
+ case 'make:controller':
+ $output .= CMakeControllerCommand::handle($this->param);
break;
default:
-
$output .= PHP_EOL;
$output .= CConsole::redbg("Command '".$this->command."' is not defined.") . PHP_EOL;
$output .= 'Type "bin/aii --help" to check all commands and options.';
diff --git a/framework/console/CHelpCommand.php b/framework/console/CHelpCommand.php
index 9351237..39677ed 100644
--- a/framework/console/CHelpCommand.php
+++ b/framework/console/CHelpCommand.php
@@ -14,9 +14,13 @@
* handle (static)
*/
-class CHelpCommand
+class CHelpCommand implements IConsoleCommand
{
+ /**
+ * Handle specific console command
+ * @return string
+ */
public static function handle()
{
$output = '';
@@ -35,9 +39,16 @@ public static function handle()
$output .= PHP_EOL;
$output .= CConsole::yellow("Available commands:") . PHP_EOL;
+
+ $output .= " ".CConsole::green("help")."\t\t\tHelp on console commands". PHP_EOL;
+
+ $output .= CConsole::yellow("cache") . PHP_EOL;
$output .= " ".CConsole::green("cache:clear")."\t\tFlush specific application cache". PHP_EOL;
$output .= " ".CConsole::green("cache:clear-all")."\tFlush all application cache". PHP_EOL;
+ $output .= CConsole::yellow("make") . PHP_EOL;
+ $output .= " ".CConsole::green("make:controller")."\tCreate controller". PHP_EOL;
+
return $output;
}
diff --git a/framework/console/CMakeControllerCommand.php b/framework/console/CMakeControllerCommand.php
new file mode 100644
index 0000000..cced01f
--- /dev/null
+++ b/framework/console/CMakeControllerCommand.php
@@ -0,0 +1,34 @@
+
+ * @link http://www.apphpframework.com/
+ * @copyright Copyright (c) 2012 - 2020 ApPHP Framework
+ * @license http://www.apphpframework.com/license/
+ *
+ * PUBLIC (static): PROTECTED: PRIVATE (static):
+ * --------------- --------------- ---------------
+ * handle (static)
+ */
+
+class CMakeControllerCommand implements IConsoleCommand
+{
+
+ /**
+ * Handle specific console command
+ *
+ * @param string $param
+ *
+ * @return string
+ */
+ public static function handle($param = '')
+ {
+ $output = 'Controller created: ' . $param;
+
+ return $output;
+ }
+
+}
diff --git a/framework/console/CVersionCommand.php b/framework/console/CVersionCommand.php
index 5004ed7..11c0e5b 100644
--- a/framework/console/CVersionCommand.php
+++ b/framework/console/CVersionCommand.php
@@ -14,9 +14,13 @@
* handle (static)
*/
-class CVersionCommand
+class CVersionCommand implements IConsoleCommand
{
+ /**
+ * Handle specific console command
+ * @return string
+ */
public static function handle()
{
$output = 'ApPHP Framework ' . CConsole::green(A::version());
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();
+}
From c66e3987bc9e3c21b33b72ec9a691c4feb46776d Mon Sep 17 00:00:00 2001
From: Samuel Akopyan <>
Date: Sat, 1 Aug 2020 23:22:30 +0300
Subject: [PATCH 26/45] Chunk implementation for AR
---
.../protected/controllers/PostsController.php | 34 ++++++----
framework/db/CActiveRecord.php | 65 +++++++++++++------
framework/messages/ar/core.php | 1 +
framework/messages/de/core.php | 1 +
framework/messages/en/core.php | 1 +
framework/messages/es/core.php | 1 +
framework/messages/fr/core.php | 1 +
framework/messages/he/core.php | 1 +
framework/messages/it/core.php | 1 +
framework/messages/nl/core.php | 3 +-
framework/messages/pl/core.php | 1 +
framework/messages/ru/core.php | 1 +
12 files changed, 76 insertions(+), 35 deletions(-)
diff --git a/demos/simple-blog/protected/controllers/PostsController.php b/demos/simple-blog/protected/controllers/PostsController.php
index cb363f8..1f04906 100644
--- a/demos/simple-blog/protected/controllers/PostsController.php
+++ b/demos/simple-blog/protected/controllers/PostsController.php
@@ -135,21 +135,27 @@ public function indexAction($msg = '')
if (!$this->_view->currentPage) {
$this->_view->actionMessage = CWidget::create('CMessage', array('error', 'Wrong parameter passed! Please try again later.', array('button' => true)));
} else {
-/* $this->_view->posts = Posts::model()->findAll(array(
- 'limit' => (($this->_view->currentPage - 1) * $this->_view->pageSize) . ', ' . $this->_view->pageSize,
- 'order' => 'post_datetime DESC',
- ));*/
+ $conditions = array(
+ 'limit' => (($this->_view->currentPage - 1) * $this->_view->pageSize) . ', ' . $this->_view->pageSize,
+ 'order' => 'post_datetime DESC',
+ );
- $posts = null;
- Posts::model()->chunk(2, function ($records) use(&$posts){
- foreach ($records as $key => $record) {
- $posts[] = $record;
- }
- //CDebug::d($record);
- });
- //CDebug::dd($posts);
-
- $this->_view->posts = $posts;
+ if (!true) {
+ $this->_view->posts = Posts::model()->findAll(array(
+ 'limit' => (($this->_view->currentPage - 1) * $this->_view->pageSize) . ', ' . $this->_view->pageSize,
+ 'order' => 'post_datetime DESC',
+ ));
+ }else{
+ $posts = null;
+ Posts::model()->chunk($conditions, [], -2, function ($records) use(&$posts){
+ foreach ($records as $key => $record) {
+ $posts[] = $record;
+ }
+ //CDebug::d($record);
+ });
+ //CDebug::dd($posts);
+ $this->_view->posts = $posts;
+ }
}
$this->_view->render('posts/index');
diff --git a/framework/db/CActiveRecord.php b/framework/db/CActiveRecord.php
index 57839ac..cb2a0a7 100644
--- a/framework/db/CActiveRecord.php
+++ b/framework/db/CActiveRecord.php
@@ -581,24 +581,46 @@ private function _createObjectFromTable()
/**
* Split AR result into parts (chunks)
+ * Ex.: chunk(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.: chunk(CConfig::get('db.prefix').$this->_tableTranslation.'.news_text LIKE :keywords', array(':keywords'=>'%'.$keywords.'%'));
+ *
+ * @param array $conditions
+ * @param array $params
* @param int $size
- * @param null $callback
+ * @param callable $callback
+ *
+ * @return null
*/
- public function chunk(int $size, callable $callback = null)
+ public function chunk(array $conditions = [], array $params = [], int $size = 0, callable $callback = null)
{
if (is_int($size) && $size > 0 && !empty($callback)) {
- $from = 0;
-// echo('limit'."$from, $size");
- while ($result = $this->findAll(array('limit'=>"$from, $size"))){
+ 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;
-// echo('limit'."$from, $size");
-// if ($from > 10){
-// return;
-// }
+ $conditions['limit'] = "$from, $size";
+
+ $count += $size;
+ if ($count >= $limitSize) {
+ break;
+ }
}
} else {
- CDebug::AddMessage('errors', 'chunk', A::t('core', 'Wrong params for chunk: {size} or callback method is callable.', array('{size}' => $size)));
+ CDebug::AddMessage('errors', 'chunk', A::t('core', 'Wrong params for chunk size: {size} or callback method is callable.', array('{size}' => $size)));
}
return null;
@@ -713,16 +735,19 @@ public function findByPk($pk, $conditions = '', $params = array(), $cacheId = fa
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
- */
+
+ /**
+ * 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
+ *
+ * @return mixed
+ */
public function findByAttributes($attributes, $conditions = '', $params = array())
{
if (is_array($conditions)) {
diff --git a/framework/messages/ar/core.php b/framework/messages/ar/core.php
index ff94c84..fc4f20e 100644
--- a/framework/messages/ar/core.php
+++ b/framework/messages/ar/core.php
@@ -186,4 +186,5 @@
'You cannot delete the last remaining record in table {table}!' => 'لا يمكنك حذف السجل الأخير المتبقي في الجدول {table}!',
'Warnings' => 'تحذيرات',
'Wrong column name: {index} in table {table}' => 'اسم عمود غير صحيح: "{index}" في الجدول "{table}" - لا يمكن الوصول إلى خاصية غير موجودة لكائن AR',
+ 'Wrong params for chunk size: {size} or callback method is callable.' => 'Wrong params for chunk size: {size} or callback method is callable.',
);
\ No newline at end of file
diff --git a/framework/messages/de/core.php b/framework/messages/de/core.php
index 79861e3..d2939ca 100644
--- a/framework/messages/de/core.php
+++ b/framework/messages/de/core.php
@@ -184,4 +184,5 @@
'You cannot delete the last remaining record in table {table}!' => 'Sie können die letzten verbliebenen Satz in der Tabelle {table} nicht löschen!',
'Warnings' => 'Warnungen',
'Wrong column name: {index} in table {table}' => 'Falsche Spalte name: {index} in Tabelle {table}',
+'Wrong params for chunk size: {size} or callback method is callable.' => 'Wrong params for chunk size: {size} or callback method is callable.',
);
\ No newline at end of file
diff --git a/framework/messages/en/core.php b/framework/messages/en/core.php
index 6e34165..162f20d 100644
--- a/framework/messages/en/core.php
+++ b/framework/messages/en/core.php
@@ -186,4 +186,5 @@
'You cannot delete the last remaining record in table {table}!' => 'You cannot delete the last remaining record in table {table}!',
'Warnings' => 'Warnings',
'Wrong column name: {index} in table {table}' => 'Wrong column name: "{index}" in table "{table}" - can\'t access non existent property of AR object',
+ 'Wrong params for chunk size: {size} or callback method is callable.' => 'Wrong params for chunk size: {size} or callback method is callable.',
);
\ No newline at end of file
diff --git a/framework/messages/es/core.php b/framework/messages/es/core.php
index d399f6b..c0b51b9 100644
--- a/framework/messages/es/core.php
+++ b/framework/messages/es/core.php
@@ -186,4 +186,5 @@
'You cannot delete the last remaining record in table {table}!' => 'No se puede eliminar el único registro existente en la tabla {table}.',
'Warnings' => 'Advertencias',
'Wrong column name: {index} in table {table}' => 'Nombre de columna incorrecta: {index} en la tabla {table}.',
+ 'Wrong params for chunk size: {size} or callback method is callable.' => 'Wrong params for chunk size: {size} or callback method is callable.',
);
\ No newline at end of file
diff --git a/framework/messages/fr/core.php b/framework/messages/fr/core.php
index 98ef71e..721bcd3 100644
--- a/framework/messages/fr/core.php
+++ b/framework/messages/fr/core.php
@@ -186,4 +186,5 @@
'You cannot delete the last remaining record in table {table}!' => 'Vous ne pouvez pas supprimer le dernier enregistrement restante dans le tableau {table}!',
'Warnings' => 'Avertissements',
'Wrong column name: {index} in table {table}' => 'Nom de colonne incorrect: {index} dans la table {table}',
+ 'Wrong params for chunk size: {size} or callback method is callable.' => 'Wrong params for chunk size: {size} or callback method is callable.',
);
\ No newline at end of file
diff --git a/framework/messages/he/core.php b/framework/messages/he/core.php
index 11d2307..364e6b7 100644
--- a/framework/messages/he/core.php
+++ b/framework/messages/he/core.php
@@ -186,4 +186,5 @@
'You cannot delete the last remaining record in table {table}!' => 'לא ניתן למחוק את הרשומה האחרונה שנותרה בטבלה {table}!',
'Warnings' => 'אזהרות',
'Wrong column name: {index} in table {table}' => 'שם עמוד שגוי: {index} בטבלה {table}',
+ 'Wrong params for chunk size: {size} or callback method is callable.' => 'Wrong params for chunk size: {size} or callback method is callable.',
);
\ No newline at end of file
diff --git a/framework/messages/it/core.php b/framework/messages/it/core.php
index ced6c4d..a9b904e 100644
--- a/framework/messages/it/core.php
+++ b/framework/messages/it/core.php
@@ -186,4 +186,5 @@
'You cannot delete the last remaining record in table {table}!' => 'Non è possibile eliminare l\'ultimo record rimasto nella tabella {table}!',
'Warnings' => 'Avvertenze',
'Wrong column name: {index} in table {table}' => 'Colonna sbagliato nome: {index} nella tabella {table}',
+ 'Wrong params for chunk size: {size} or callback method is callable.' => 'Wrong params for chunk size: {size} or callback method is callable.',
);
\ No newline at end of file
diff --git a/framework/messages/nl/core.php b/framework/messages/nl/core.php
index 3d06763..1ce9f5a 100644
--- a/framework/messages/nl/core.php
+++ b/framework/messages/nl/core.php
@@ -181,9 +181,10 @@
'Unable to find the model class "{model}".' => 'Kan het model de klas "{model}".',
'Unknown' => 'Onbekend',
'Unknown operator "{operator}".' => 'Onbekende operator "{operator}".',
- 'Uploaded {file} is not a valid image! Please check carefully the file type.' => 'Ge�ploade file {file} is geen geldige afbeelding! Gelieve zorgvuldig te controleren het bestandstype.',
+ 'Uploaded {file} is not a valid image! Please check carefully the file type.' => 'Geploade file {file} is geen geldige afbeelding! Gelieve zorgvuldig te controleren het bestandstype.',
'version' => 'versie',
'You cannot delete the last remaining record in table {table}!' => 'U kunt de laatst overgebleven record niet verwijderen in tabel {table}!',
'Warnings' => 'Waarschuwingen',
'Wrong column name: {index} in table {table}' => 'naam verkeerd column: {index} in tabel {table}',
+ 'Wrong params for chunk size: {size} or callback method is callable.' => 'Wrong params for chunk size: {size} or callback method is callable.',
);
\ No newline at end of file
diff --git a/framework/messages/pl/core.php b/framework/messages/pl/core.php
index 0a70a8c..0c14653 100644
--- a/framework/messages/pl/core.php
+++ b/framework/messages/pl/core.php
@@ -186,4 +186,5 @@
'You cannot delete the last remaining record in table {table}!' => 'Nie możesz skasować ostatniego rekordu który pozostał {table}!',
'Warnings' => 'Ostrzeżenia',
'Wrong column name: {index} in table {table}' => 'Zła nazwa kolumny: {index} w tablicy {table}',
+ 'Wrong params for chunk size: {size} or callback method is callable.' => 'Wrong params for chunk size: {size} or callback method is callable.',
);
\ No newline at end of file
diff --git a/framework/messages/ru/core.php b/framework/messages/ru/core.php
index 60f6b41..0957afd 100644
--- a/framework/messages/ru/core.php
+++ b/framework/messages/ru/core.php
@@ -186,4 +186,5 @@
'You cannot delete the last remaining record in table {table}!' => 'Вы не можете удалить последнюю оставшуюся запись в таблице {table}!',
'Warnings' => 'Предупреждения',
'Wrong column name: {index} in table {table}' => 'Неверное имя колонки: {index} в таблице {table}',
+ 'Wrong params for chunk size: {size} or callback method is callable.' => 'Wrong params for chunk size: {size} or callback method is callable.',
);
\ No newline at end of file
From 6b1f76a61e90ff378877e234a29cdc9a751bc4cd Mon Sep 17 00:00:00 2001
From: Samuel Akopyan <>
Date: Sat, 1 Aug 2020 23:42:16 +0300
Subject: [PATCH 27/45] Redo array() to []
---
.../protected/components/Bootstrap.php | 18 +-
.../controllers/CategoriesController.php | 81 +-
.../protected/controllers/PostsController.php | 2 +-
demos/simple-blog/protected/models/Posts.php | 42 +-
.../modules/setup/templates/default.php | 32 +-
.../modules/setup/views/setup/index.php | 28 +-
.../protected/views/categories/index.php | 25 +-
.../protected/views/categories/view.php | 22 +-
.../controllers/AdminsController.php | 353 +-
.../setup/templates/views/setup/database.php | 8 +-
framework/components/CClientScript.php | 1150 +++---
framework/components/CComponent.php | 103 +-
framework/components/CDbHttpSession.php | 842 +++--
framework/components/CHttpCookie.php | 289 +-
framework/components/CHttpRequest.php | 2009 +++++-----
framework/components/CHttpSession.php | 585 +--
framework/components/CLocalTime.php | 1274 +++++--
framework/components/CLogger.php | 298 +-
framework/components/CMessageSource.php | 196 +-
framework/components/CMobileDetect.php | 50 +-
framework/components/CShoppingCart.php | 890 ++---
framework/components/CUri.php | 651 ++--
framework/console/CCacheClearCommand.php | 6 +-
framework/console/CConsole.php | 16 +-
framework/console/CConsoleCommand.php | 9 +-
framework/console/CHelpCommand.php | 27 +-
framework/console/CMakeControllerCommand.php | 2 +-
framework/console/CVersionCommand.php | 3 +-
framework/db/CActiveRecord.php | 3229 +++++++++--------
framework/db/CDatabase.php | 1996 +++++-----
framework/db/CDbCommand.php | 1592 ++++----
framework/db/CRecordEntity.php | 433 ++-
framework/helpers/widgets/CBreadCrumbs.php | 133 +-
.../helpers/widgets/CLanguageSelector.php | 219 +-
framework/helpers/widgets/CPagination.php | 398 +-
framework/helpers/widgets/CTabs.php | 252 +-
tests/framework/helpers/CArrayTest.php | 192 +-
tests/framework/helpers/CAuthTest.php | 6 +-
tests/framework/helpers/CValidatorTest.php | 70 +-
39 files changed, 9594 insertions(+), 7937 deletions(-)
diff --git a/demos/simple-blog/protected/components/Bootstrap.php b/demos/simple-blog/protected/components/Bootstrap.php
index 76fa107..51f8e6e 100644
--- a/demos/simple-blog/protected/components/Bootstrap.php
+++ b/demos/simple-blog/protected/components/Bootstrap.php
@@ -24,15 +24,15 @@ class Bootstrap extends CComponent
*/
function __construct()
{
- A::app()->attachEventHandler('_onBeginRequest', array($this, 'setTimeZone'));
- A::app()->attachEventHandler('_onBeginRequest', array($this, 'setSslMode'));
- A::app()->attachEventHandler('_onBeginRequest', array($this, 'setCron'));
-
- A::app()->attachEventHandler('_onEndRequest', array($this, 'setLastVisitedPage'));
- }
-
- /**
- * Returns the instance of object
+ A::app()->attachEventHandler('_onBeginRequest', [$this, 'setTimeZone']);
+ A::app()->attachEventHandler('_onBeginRequest', [$this, 'setSslMode']);
+ A::app()->attachEventHandler('_onBeginRequest', [$this, 'setCron']);
+
+ A::app()->attachEventHandler('_onEndRequest', [$this, 'setLastVisitedPage']);
+ }
+
+ /**
+ * Returns the instance of object
* @return current class
*/
public static function init()
diff --git a/demos/simple-blog/protected/controllers/CategoriesController.php b/demos/simple-blog/protected/controllers/CategoriesController.php
index de9be60..88a4687 100644
--- a/demos/simple-blog/protected/controllers/CategoriesController.php
+++ b/demos/simple-blog/protected/controllers/CategoriesController.php
@@ -52,8 +52,8 @@ public function viewAction($categoryId = 0)
//All posts from the selected category
$postsModel = Posts::model();
- if (!$postsModel->count('category_id = :category_id', array(':category_id' => $categoryId))) {
- $msgType = 'warning';
+ if ( ! $postsModel->count('category_id = :category_id', [':category_id' => $categoryId])) {
+ $msgType = 'warning';
$msg = (!empty($catName)) ? 'There are still no posts in category ' . $catName . ' .' : 'Wrong parameter passed, please try again later.';
} else {
@@ -61,25 +61,26 @@ public function viewAction($categoryId = 0)
$this->_view->targetPage = 'categories/view/id/' . $categoryId;
$this->_view->currentPage = A::app()->getRequest()->getQuery('page', 'integer', 1);
$this->_view->pageSize = '5';
- $this->_view->totalRecords = Posts::model()->count(array(
- 'condition' => 'category_id = :category_id',
- ),
- array(':category_id' => $categoryId)
- );
-
- $msgType = 'info';
+ $this->_view->totalRecords = Posts::model()->count(
+ ['condition' => 'category_id = :category_id',],
+ [':category_id' => $categoryId]
+ );
+
+ $msgType = 'info';
$msg = 'Category: ' . $catName;
-
- $this->_view->posts = $postsModel->findAll(array(
- 'condition' => 'category_id = :category_id',
- 'limit' => (($this->_view->currentPage - 1) * $this->_view->pageSize) . ', ' . $this->_view->pageSize,
- 'order' => 'post_datetime DESC',
- ),
- array(':category_id' => $categoryId)
- );
- }
- $this->_view->mainText = CWidget::create('CMessage', array($msgType, $msg, array('button' => false)));
- $this->_view->render('categories/view');
+
+ $this->_view->posts = $postsModel->findAll(
+ [
+ 'condition' => 'category_id = :category_id',
+ 'limit' => (($this->_view->currentPage - 1) * $this->_view->pageSize).', '
+ .$this->_view->pageSize,
+ 'order' => 'post_datetime DESC',
+ ],
+ [':category_id' => $categoryId]
+ );
+ }
+ $this->_view->mainText = CWidget::create('CMessage', [$msgType, $msg, ['button' => false]]);
+ $this->_view->render('categories/view');
}
public function indexAction($msg = '')
@@ -107,25 +108,35 @@ public function indexAction($msg = '')
$msgType = 'Wrong parameter passed! Check category ID.';
$msgType = 'error';
}
- if (!empty($msgType)) $this->_view->actionMessage = CWidget::create('CMessage', array($msgType, $msgType, array('button' => true)));
- }
-
- // prepare pagination vars
+ if ( ! empty($msgType)) {
+ $this->_view->actionMessage = CWidget::create('CMessage', [$msgType, $msgType, ['button' => true]]);
+ }
+ }
+
+ // prepare pagination vars
$this->_view->targetPage = 'categories/index';
$this->_view->currentPage = A::app()->getRequest()->getQuery('page', 'integer', 1);
$this->_view->pageSize = '15';
$this->_view->totalRecords = Categories::model()->count();
-
- if (!$this->_view->currentPage) {
- $this->_view->actionMessage = CWidget::create('CMessage', array('error', 'Wrong parameter passed! Please try again later.', array('button' => true)));
- } else {
- $this->_view->categories = Categories::model()->findAll(array(
- 'limit' => (($this->_view->currentPage - 1) * $this->_view->pageSize) . ', ' . $this->_view->pageSize,
- 'order' => 'id ASC',
- ));
- }
-
- $this->_view->render('categories/index');
+
+ if ( ! $this->_view->currentPage) {
+ $this->_view->actionMessage = CWidget::create('CMessage',
+ [
+ 'error',
+ 'Wrong parameter passed! Please try again later.',
+ ['button' => true]
+ ]
+ );
+ } else {
+ $this->_view->categories = Categories::model()->findAll(
+ [
+ 'limit' => (($this->_view->currentPage - 1) * $this->_view->pageSize).', '.$this->_view->pageSize,
+ 'order' => 'id ASC',
+ ]
+ );
+ }
+
+ $this->_view->render('categories/index');
}
public function addAction()
diff --git a/demos/simple-blog/protected/controllers/PostsController.php b/demos/simple-blog/protected/controllers/PostsController.php
index 1f04906..b6e00c0 100644
--- a/demos/simple-blog/protected/controllers/PostsController.php
+++ b/demos/simple-blog/protected/controllers/PostsController.php
@@ -147,7 +147,7 @@ public function indexAction($msg = '')
));
}else{
$posts = null;
- Posts::model()->chunk($conditions, [], -2, function ($records) use(&$posts){
+ Posts::model()->chunk($conditions, [], 2, function ($records) use(&$posts){
foreach ($records as $key => $record) {
$posts[] = $record;
}
diff --git a/demos/simple-blog/protected/models/Posts.php b/demos/simple-blog/protected/models/Posts.php
index 549b86e..9d08728 100644
--- a/demos/simple-blog/protected/models/Posts.php
+++ b/demos/simple-blog/protected/models/Posts.php
@@ -19,12 +19,12 @@ class Posts extends CActiveRecord
public $categoryOldId;
/** @var */
- protected $_fillable = array();
- /** @var */
- protected $_guarded = array('post_datetime');
+ protected $_fillable = [];
+ /** @var */
+ protected $_guarded = ['post_datetime'];
-
- public function __construct()
+
+ public function __construct()
{
parent::__construct();
}
@@ -42,13 +42,27 @@ public static function model()
*/
protected function _relations()
{
- return array(
- 'author_id' => array(self::HAS_ONE, 'authors', 'id', 'condition' => '', 'joinType' => self::LEFT_OUTER_JOIN, 'fields' => array('login' => '')),
- 'category_id' => array(self::BELONGS_TO, 'categories', 'id', 'condition' => '', 'joinType' => self::LEFT_OUTER_JOIN, 'fields' => array('name' => 'category_name')),
- );
- }
-
- protected function _afterSave($pk = '')
+ return [
+ 'author_id' => [
+ self::HAS_ONE,
+ 'authors',
+ 'id',
+ 'condition' => '',
+ 'joinType' => self::LEFT_OUTER_JOIN,
+ 'fields' => ['login' => '']
+ ],
+ 'category_id' => [
+ self::BELONGS_TO,
+ 'categories',
+ 'id',
+ 'condition' => '',
+ 'joinType' => self::LEFT_OUTER_JOIN,
+ 'fields' => ['name' => 'category_name']
+ ],
+ ];
+ }
+
+ protected function _afterSave($pk = '')
{
if ($this->categoryOldId != $this->category_id) {
$this->_updatePostsCount($this->categoryOldId);
@@ -65,7 +79,7 @@ protected function _afterDelete($pk = '')
private function _updatePostsCount($pKey)
{
// update total count of posts in categories table
- $totalPosts = self::model()->count('category_id = :category_id', array(':category_id' => $pKey));
- $this->_db->update('categories', array('posts_count' => $totalPosts), 'id = ' . (int)$pKey);
+ $totalPosts = self::model()->count('category_id = :category_id', [':category_id' => $pKey]);
+ $this->_db->update('categories', array('posts_count' => $totalPosts), 'id = ' . (int)$pKey);
}
}
diff --git a/demos/simple-blog/protected/modules/setup/templates/default.php b/demos/simple-blog/protected/modules/setup/templates/default.php
index 1a3982a..2496d87 100644
--- a/demos/simple-blog/protected/modules/setup/templates/default.php
+++ b/demos/simple-blog/protected/modules/setup/templates/default.php
@@ -27,21 +27,25 @@
'vertical',
- 'items'=>array(
- array('label'=>'1. Server Requirements', 'url'=>'setup/index', 'readonly'=>true),
- array('label'=>'2. Database Settings', 'url'=>'setup/database', 'readonly'=>true),
- array('label'=>'3. Administrator Account', 'url'=>'setup/administrator', 'readonly'=>true),
- array('label'=>'4. Ready to Install', 'url'=>'setup/ready', 'readonly'=>true),
- array('label'=>'5. Completed', 'url'=>'setup/completed', 'readonly'=>true),
- ),
- 'selected'=>$this->_activeMenu,
- 'return'=>false
- ));
+
+ CWidget::create(
+ 'CMenu',
+ [
+ 'type' => 'vertical',
+ 'items' => [
+ ['label' => '1. Server Requirements', 'url' => 'setup/index', 'readonly' => true],
+ ['label' => '2. Database Settings', 'url' => 'setup/database', 'readonly' => true],
+ ['label' => '3. Administrator Account', 'url' => 'setup/administrator', 'readonly' => true],
+ ['label' => '4. Ready to Install', 'url' => 'setup/ready', 'readonly' => true],
+ ['label' => '5. Completed', 'url' => 'setup/completed', 'readonly' => true],
+ ],
+ 'selected' => $this->_activeMenu,
+ 'return' => false
+ ]
+ );
?>
-
-
+
+
view->getContent(); ?>
diff --git a/demos/simple-blog/protected/modules/setup/views/setup/index.php b/demos/simple-blog/protected/modules/setup/views/setup/index.php
index 6a70e66..d1fbcec 100644
--- a/demos/simple-blog/protected/modules/setup/views/setup/index.php
+++ b/demos/simple-blog/protected/modules/setup/views/setup/index.php
@@ -10,17 +10,21 @@
= $actionMessage; ?>
'setup/index',
- 'method' => 'post',
- 'htmlOptions' => array(
- 'name' => 'frmSetup',
- ),
- 'fields' => $formFields,
- 'buttons' => array(
- 'submit' => array('type' => 'submit', 'value' => A::t('setup', 'Next'), 'htmlOptions' => array('name' => '')),
- ),
- 'return' => true,
-));
+
+echo CWidget::create(
+ 'CFormView',
+ [
+ 'action' => 'setup/index',
+ 'method' => 'post',
+ 'htmlOptions' => [
+ 'name' => 'frmSetup',
+ ],
+ 'fields' => $formFields,
+ 'buttons' => [
+ 'submit' => ['type' => 'submit', 'value' => A::t('setup', 'Next'), 'htmlOptions' => ['name' => '']],
+ ],
+ 'return' => true,
+ ]
+);
?>
diff --git a/demos/simple-blog/protected/views/categories/index.php b/demos/simple-blog/protected/views/categories/index.php
index 5c47b1d..6c49c04 100644
--- a/demos/simple-blog/protected/views/categories/index.php
+++ b/demos/simple-blog/protected/views/categories/index.php
@@ -31,16 +31,19 @@
}
echo '';
echo '';
-
- echo CWidget::create('CPagination', array(
- 'actionPath' => 'categories/index',
- 'currentPage' => $currentPage,
- 'pageSize' => $pageSize,
- 'totalRecords' => $totalRecords,
- 'linkType' => 1,
- 'paginationType' => 'fullNumbers',
- ));
- }
- ?>
+
+ echo CWidget::create(
+ 'CPagination',
+ [
+ 'actionPath' => 'categories/index',
+ 'currentPage' => $currentPage,
+ 'pageSize' => $pageSize,
+ 'totalRecords' => $totalRecords,
+ 'linkType' => 1,
+ 'paginationType' => 'fullNumbers',
+ ]
+ );
+ }
+ ?>
diff --git a/demos/simple-blog/protected/views/categories/view.php b/demos/simple-blog/protected/views/categories/view.php
index cabba2f..1d0220e 100644
--- a/demos/simple-blog/protected/views/categories/view.php
+++ b/demos/simple-blog/protected/views/categories/view.php
@@ -47,17 +47,19 @@
}
if (count($posts) > 1) {
- echo CWidget::create('CPagination', array(
- 'actionPath' => 'posts/view',
- 'currentPage' => $currentPage,
- 'pageSize' => $pageSize,
- 'totalRecords' => $totalRecords,
- 'showResultsOfTotal' => false,
- 'linkType' => 0,
- 'paginationType' => 'prevNext',
- ));
+ echo CWidget::create(
+ 'CPagination',
+ [
+ 'actionPath' => 'posts/view',
+ 'currentPage' => $currentPage,
+ 'pageSize' => $pageSize,
+ 'totalRecords' => $totalRecords,
+ 'showResultsOfTotal' => false,
+ 'linkType' => 0,
+ 'paginationType' => 'prevNext',
+ ]
+ );
}
-
}
?>
diff --git a/demos/simple-cms/protected/controllers/AdminsController.php b/demos/simple-cms/protected/controllers/AdminsController.php
index 9864918..d4ce082 100644
--- a/demos/simple-cms/protected/controllers/AdminsController.php
+++ b/demos/simple-cms/protected/controllers/AdminsController.php
@@ -12,178 +12,187 @@
* addAction
* deleteAction
*/
+
class AdminsController extends CController
{
- public function __construct()
- {
- parent::__construct();
-
- // block access to this controller for not-logged users
- CAuth::handleLogin();
-
- $this->_loggedId = CAuth::getLoggedId();
-
- $settings = Settings::model()->findByPk(1);
- $this->_view->setMetaTags('title', 'Account | ' . $settings->metatag_title);
- $this->_view->setMetaTags('keywords', $settings->metatag_keywords);
- $this->_view->setMetaTags('description', $settings->metatag_description);
- $this->_view->cmsName = $settings->site_name;
- $this->_view->cmsSlogan = $settings->slogan;
- $this->_view->cmsFooter = $settings->footer;
-
- $this->_view->activeLink = 'home';
- $this->_view->viewRightMenu = false;
- $this->_view->errorField = '';
- $this->_view->actionMessage = '';
-
- // prepare list of roles that the logged admin can deal with
- $allRolesList = array();
- $rolesList = array();
-
- if (CAuth::isLoggedInAs('owner')) {
- $rolesList = array('mainadmin' => 'mainadmin', 'admin' => 'admin');
- } elseif (CAuth::isLoggedInAs('mainadmin')) {
- $rolesList = array('admin' => 'admin');
- }
- $roles = array(
- array('code' => 'owner', 'name' => 'Owner'),
- array('code' => 'mainadmin', 'name' => 'Main Admin'),
- array('code' => 'admin', 'name' => 'Admin'),
- );
- if (is_array($roles)) {
- foreach ($roles as $role) {
- $allRolesList[$role['code']] = $role['name'];
- if (in_array($role['code'], $rolesList)) {
- $rolesList[$role['code']] = $role['name'];
- }
- }
- }
- $this->_view->rolesListStr = "'" . implode("','", array_keys($rolesList)) . "'";
- $this->_view->rolesList = $rolesList;
- $this->_view->allRolesList = $allRolesList;
-
- $this->_view->dateTimeFormat = 'm F, Y H:i:s';
- }
-
- public function indexAction()
- {
- $this->redirect('admins/view');
- }
-
- /**
- * View admins action handler
- * @param string $msg
- */
- public function viewAction($msg = '')
- {
- $this->_view->activeLink = 'admins';
- switch ($msg) {
- case 'added':
- $message = A::t('core', 'The adding operation has been successfully completed!');
- break;
- case 'updated':
- $message = A::t('core', 'The updating operation has been successfully completed!');
- break;
- default:
- $message = '';
- }
- if (!empty($message)) {
- $this->_view->actionMessage = CWidget::create('CMessage', array('success', $message, array('button' => true)));
- }
- $this->_view->render('admins/view');
- }
-
- /**
- * Edit admin action handler
- * @param int $id The admin id
- */
- public function editAction($id = 0)
- {
- $this->_view->activeLink = 'admins';
-
- //$this->_view->activeLink = 'admins';
- $admin = Admins::model()->findByPk((int)$id);
- if (!$admin) {
- $this->redirect('backend/index');
- }
- $this->_view->isMyAccount = ($admin->id == $this->_loggedId ? true : false);
- if ($this->_view->isMyAccount == true) $this->_view->activeLink = 'myAccount';
-
- // allow access to edit other admins only to site owner or main admin
- if (!$this->_view->isMyAccount &&
- !CAuth::isLoggedInAs('owner', 'mainadmin') &&
- !in_array($admin->role, array_keys($this->_view->rolesList))) {
- $this->redirect('backend/index');
- }
- $this->_view->admin = $admin;
- $this->_view->password = '';
- $this->_view->passwordRetype = '';
-
- $this->_view->render('admins/edit');
- }
-
-
- /**
- * My Account action handler
- * Calls the editAction with id of logged admin.
- */
- public function myAccountAction()
- {
- $this->_view->activeLink = 'myAccount';
- $this->editAction($this->_loggedId);
- }
-
- /*
- * Add new admin action handler
- */
- public function addAction()
- {
- // allow access only to site owner or main admin
- if (!CAuth::isLoggedInAs('owner', 'mainadmin')) {
- $this->redirect('backend/index');
- }
- $this->_view->render('admins/add');
- }
-
- /**
- * Delete admin action handler
- * @param int $id The admin id
- */
- public function deleteAction($id = 0)
- {
- // allow access only to site owner or main admin
- if (!CAuth::isLoggedInAs('owner', 'mainadmin')) {
- $this->redirect('backend/index');
- }
-
- $msg = '';
- $msgType = '';
-
- $admin = Admins::model()->findByPk((int)$id);
- if (!$admin) {
- $this->redirect('admins/view');
- }
-
- // check if this delete operation is allowed
- if (!in_array($admin->role, array_keys($this->_view->rolesList))) {
- $msg = A::t('core', 'Operation Blocked Error Message');
- $msgType = 'error';
- // delete the admin
- } elseif ($admin->delete()) {
- $msg = A::t('core', 'Deleting operation has been successfully completed!');
- $msgType = 'success';
- } else {
- if (APPHP_MODE == 'demo') {
- $msg = CDatabase::init()->getErrorMessage();
- } else {
- $msg = A::t('core', 'An error occurred while deleting the record!');
- }
- $msgType = 'error';
- }
- if (!empty($msg)) {
- $this->_view->actionMessage = CWidget::create('CMessage', array($msgType, $msg, array('button' => true)));
- }
- $this->_view->render('admins/view');
- }
-
+ public function __construct()
+ {
+ parent::__construct();
+
+ // block access to this controller for not-logged users
+ CAuth::handleLogin();
+
+ $this->_loggedId = CAuth::getLoggedId();
+
+ $settings = Settings::model()->findByPk(1);
+ $this->_view->setMetaTags('title', 'Account | '.$settings->metatag_title);
+ $this->_view->setMetaTags('keywords', $settings->metatag_keywords);
+ $this->_view->setMetaTags('description', $settings->metatag_description);
+ $this->_view->cmsName = $settings->site_name;
+ $this->_view->cmsSlogan = $settings->slogan;
+ $this->_view->cmsFooter = $settings->footer;
+
+ $this->_view->activeLink = 'home';
+ $this->_view->viewRightMenu = false;
+ $this->_view->errorField = '';
+ $this->_view->actionMessage = '';
+
+ // prepare list of roles that the logged admin can deal with
+ $allRolesList = [];
+ $rolesList = [];
+
+ if (CAuth::isLoggedInAs('owner')) {
+ $rolesList = ['mainadmin' => 'mainadmin', 'admin' => 'admin'];
+ } elseif (CAuth::isLoggedInAs('mainadmin')) {
+ $rolesList = ['admin' => 'admin'];
+ }
+ $roles = [
+ ['code' => 'owner', 'name' => 'Owner'],
+ ['code' => 'mainadmin', 'name' => 'Main Admin'],
+ ['code' => 'admin', 'name' => 'Admin'],
+ ];
+ if (is_array($roles)) {
+ foreach ($roles as $role) {
+ $allRolesList[$role['code']] = $role['name'];
+ if (in_array($role['code'], $rolesList)) {
+ $rolesList[$role['code']] = $role['name'];
+ }
+ }
+ }
+ $this->_view->rolesListStr = "'".implode("','", array_keys($rolesList))."'";
+ $this->_view->rolesList = $rolesList;
+ $this->_view->allRolesList = $allRolesList;
+
+ $this->_view->dateTimeFormat = 'm F, Y H:i:s';
+ }
+
+ public function indexAction()
+ {
+ $this->redirect('admins/view');
+ }
+
+ /**
+ * View admins action handler
+ *
+ * @param string $msg
+ */
+ public function viewAction($msg = '')
+ {
+ $this->_view->activeLink = 'admins';
+ switch ($msg) {
+ case 'added':
+ $message = A::t('core', 'The adding operation has been successfully completed!');
+ break;
+ case 'updated':
+ $message = A::t('core', 'The updating operation has been successfully completed!');
+ break;
+ default:
+ $message = '';
+ }
+ if ( ! empty($message)) {
+ $this->_view->actionMessage = CWidget::create('CMessage', ['success', $message, ['button' => true]]);
+ }
+ $this->_view->render('admins/view');
+ }
+
+ /**
+ * Edit admin action handler
+ *
+ * @param int $id The admin id
+ */
+ public function editAction($id = 0)
+ {
+ $this->_view->activeLink = 'admins';
+
+ //$this->_view->activeLink = 'admins';
+ $admin = Admins::model()->findByPk((int)$id);
+ if ( ! $admin) {
+ $this->redirect('backend/index');
+ }
+ $this->_view->isMyAccount = ($admin->id == $this->_loggedId ? true : false);
+ if ($this->_view->isMyAccount == true) {
+ $this->_view->activeLink = 'myAccount';
+ }
+
+ // allow access to edit other admins only to site owner or main admin
+ if ( ! $this->_view->isMyAccount
+ &&
+ ! CAuth::isLoggedInAs('owner', 'mainadmin')
+ &&
+ ! in_array($admin->role, array_keys($this->_view->rolesList))
+ ) {
+ $this->redirect('backend/index');
+ }
+ $this->_view->admin = $admin;
+ $this->_view->password = '';
+ $this->_view->passwordRetype = '';
+
+ $this->_view->render('admins/edit');
+ }
+
+
+ /**
+ * My Account action handler
+ * Calls the editAction with id of logged admin.
+ */
+ public function myAccountAction()
+ {
+ $this->_view->activeLink = 'myAccount';
+ $this->editAction($this->_loggedId);
+ }
+
+ /*
+ * Add new admin action handler
+ */
+ public function addAction()
+ {
+ // allow access only to site owner or main admin
+ if ( ! CAuth::isLoggedInAs('owner', 'mainadmin')) {
+ $this->redirect('backend/index');
+ }
+ $this->_view->render('admins/add');
+ }
+
+ /**
+ * Delete admin action handler
+ *
+ * @param int $id The admin id
+ */
+ public function deleteAction($id = 0)
+ {
+ // allow access only to site owner or main admin
+ if ( ! CAuth::isLoggedInAs('owner', 'mainadmin')) {
+ $this->redirect('backend/index');
+ }
+
+ $msg = '';
+ $msgType = '';
+
+ $admin = Admins::model()->findByPk((int)$id);
+ if ( ! $admin) {
+ $this->redirect('admins/view');
+ }
+
+ // check if this delete operation is allowed
+ if ( ! in_array($admin->role, array_keys($this->_view->rolesList))) {
+ $msg = A::t('core', 'Operation Blocked Error Message');
+ $msgType = 'error';
+ // delete the admin
+ } elseif ($admin->delete()) {
+ $msg = A::t('core', 'Deleting operation has been successfully completed!');
+ $msgType = 'success';
+ } else {
+ if (APPHP_MODE == 'demo') {
+ $msg = CDatabase::init()->getErrorMessage();
+ } else {
+ $msg = A::t('core', 'An error occurred while deleting the record!');
+ }
+ $msgType = 'error';
+ }
+ if ( ! empty($msg)) {
+ $this->_view->actionMessage = CWidget::create('CMessage', [$msgType, $msg, ['button' => true]]);
+ }
+ $this->_view->render('admins/view');
+ }
+
}
\ No newline at end of file
diff --git a/demos/simple-cms/protected/modules/setup/templates/views/setup/database.php b/demos/simple-cms/protected/modules/setup/templates/views/setup/database.php
index e8290f4..bccdff4 100644
--- a/demos/simple-cms/protected/modules/setup/templates/views/setup/database.php
+++ b/demos/simple-cms/protected/modules/setup/templates/views/setup/database.php
@@ -21,8 +21,12 @@
),
'fields'=>$formFields,
'buttons'=>array(
- 'back'=>array('type'=>'button', 'value'=>'Previous', 'htmlOptions'=>array('name'=>'', 'onclick'=>"$(location).attr('href','setup/index');")),
- 'submit'=>array('type'=>'submit', 'value'=>'Next', 'htmlOptions'=>array('name'=>''))
+ 'back' => array(
+ 'type' => 'button',
+ 'value' => 'Previous',
+ 'htmlOptions' => array('name' => '', 'onclick' => "$(location).attr('href','setup/index');")
+ ),
+ 'submit' => array('type' => 'submit', 'value' => 'Next', 'htmlOptions' => array('name' => ''))
),
'events'=>array(
'focus'=>array('field'=>$errorField)
diff --git a/framework/components/CClientScript.php b/framework/components/CClientScript.php
index 84cd838..7cc2d5c 100644
--- a/framework/components/CClientScript.php
+++ b/framework/components/CClientScript.php
@@ -29,532 +29,626 @@
class CClientScript extends CComponent
{
- /** The script is rendered in the */
- const POS_HEAD = 0;
- /** The script is rendered at the beginning of the */
- const POS_BODY_BEGIN = 1;
- /** The script is rendered at the end of the */
- const POS_BODY_END = 2;
- /** The script is rendered inside window onload function */
- const POS_ON_LOAD = 3;
- /** The body script is rendered inside a jQuery ready function */
- const POS_JQUERY_READY = 4;
- /** The script is rendered inside document ready function */
- const POS_DOC_READY = 5;
-
- /** The limit in amount of css minify files */
- const CSS_MINIFY_LIMIT = 100;
- /** The limit in amount of js minify files */
- const JS_MINIFY_LIMIT = 100;
-
- /** @var boolean */
- public $enableJavaScript = true;
- /** @var array */
- protected $_cssFiles = array();
- /** @var int */
- protected $_countCompressedCssFiles = 0;
- /** @var array */
- protected $_css = array();
- /** @var array */
- protected $_scriptFiles = array();
- /** @var int */
- protected $_countCompressedJsFiles = 0;
- /** @var array */
- protected $_scripts = array();
- /** @var boolean */
- protected $_hasStyles = false;
- /** @var boolean */
- protected $_hasScripts = false;
- /** @var string */
- protected $_countJsFiles = 0;
- /** @var string */
- protected $_countCssFiles = 0;
-
-
- /**
- * Class default constructor
- */
- function __construct()
- {
-
- }
-
- /**
- * Returns the instance of object
- * @return current class
- */
- public static function init()
- {
- return parent::init(__CLASS__);
- }
-
- /**
- * Registers a piece of CSS code
- * @param string $id
- * @param string $css
- * @param string $media
- * @return void
- */
- public function registerCss($id, $css, $media = '')
- {
- if (!empty($css)) {
- if (!$this->_hasStyles) $this->_hasStyles = true;
- $this->_css[$id] = array($css, $media);
- }
- }
-
- /**
- * Registers CSS file
- * @param string $url
- * @param string $media
- * @param string $level
- * @return void
- */
- public function registerCssFile($url, $media = 'all', $level = '')
- {
- if (!empty($url)) {
- if (!$this->_hasStyles) $this->_hasStyles = true;
- if (!isset($this->_cssFiles[$media])) $this->_cssFiles[$media] = array();
- if (!in_array($url, $this->_cssFiles[$media])) {
- if (!empty($level)) {
- if (isset($this->_cssFiles[$media][$level])) {
- CDebug::addMessage('warnings', 'css-file-exists', A::t('core', 'CSS file with the same level "{level}" already registered: "{file}"', array('{level}' => $level, '{file}' => $url)));
- }
- $this->_cssFiles[$media][$level] = $url;
- } else {
- $this->_cssFiles[$media][$this->_countCssFiles++] = $url;
- }
- }
- }
- }
-
- /**
- * Registers CSS files
- * @param string $urls
- * @param string $path
- * @return void
- */
- public function registerCssFiles($urls, $path = '')
- {
- if (!empty($urls) && array($urls)) {
- foreach ($urls as $key => $url) {
- if (empty($url)) continue;
- $path = !empty($path) ? trim($path, '/') . '/' : '';
- $url = $path . (is_array($url) ? $key : $url);
- $media = (is_array($url) && !empty($url['media'])) ? ' media="' . $url['media'] . '"' : 'all';
- $this->registerCssFile($url, $media);
- }
- }
- }
-
- /**
- * Registers a piece of javascript code
- * @param string $id
- * @param string $script
- * @param integer $position
- * @return void
- */
- public function registerScript($id, $script, $position = self::POS_JQUERY_READY)
- {
- if (!empty($script)) {
- if (!$this->_hasScripts) $this->_hasScripts = true;
- $this->_scripts[$position][$id] = $script;
- if ($position === self::POS_JQUERY_READY || $position === self::POS_ON_LOAD) {
- $this->registerCoreScript('jquery');
- }
- }
- }
-
- /**
- * Registers javascript file
- * @param string $url
- * @param integer $position self::POS_HEAD|self::POS_BODY_BEGIN|self::POS_BODY_END
- * @param string $level
- * @return void
- */
- public function registerScriptFile($url, $position = self::POS_HEAD, $level = '')
- {
- if (!empty($url)) {
- if (!$this->_hasScripts) $this->_hasScripts = true;
- if (!isset($this->_scriptFiles[$position])) $this->_scriptFiles[$position] = array();
- if (!in_array($url, $this->_scriptFiles[$position])) {
- if (!empty($level)) {
- if (isset($this->_scriptFiles[$position][$level])) {
- CDebug::addMessage('warnings', 'js-file-exists', A::t('core', 'Javascript file with the same level "{level}" already registered: "{file}"', array('{level}' => $level, '{file}' => $url)));
- }
- $this->_scriptFiles[$position][$level] = $url;
- } else {
- $this->_scriptFiles[$position][$this->_countJsFiles++] = $url;
- }
- }
- }
- }
-
- /**
- * Registers a required javascript file
- * @param string $url
- * @param string $path
- * @param integer $position self::POS_HEAD|self::POS_BODY_BEGIN|self::POS_BODY_END
- * @return void
- */
- public function registerScriptFiles($urls, $path = '', $position = self::POS_HEAD)
- {
- if (!empty($urls) && array($urls)) {
- foreach ($urls as $key => $url) {
- $path = !empty($path) ? trim($path, '/') . '/' : '';
- $url = $path . (is_array($url) ? $key : $url);
- $position = (is_array($url) && !empty($url['position'])) ? $url['position'] : $position;
- $this->registerScriptFile($url, $position);
- }
- }
- }
-
- /**
- * Registers a core script package
- * @param string $name
- * @return void
- */
- public function registerCoreScript($name)
- {
- // Registers core script
- }
-
- /**
- * Renders the registered scripts in our class
- * This method is called in View->render() class
- * @param string &$output
- * @return void|bool
- */
- public function render(&$output)
- {
- if (!$this->_hasStyles && !$this->_hasScripts) {
- return false;
- }
-
- // Sort CSS according by level
- $this->_sortByLevel($this->_cssFiles);
-
- // Sort JS scripts by level
- if ($this->enableJavaScript) {
- $this->_sortByLevel($this->_scriptFiles);
- }
-
- $this->_cssToCompression();
- $this->_jsToCompression();
- $this->renderHead($output);
- if ($this->enableJavaScript) {
- $this->renderBodyBegin($output);
- $this->renderBodyEnd($output);
- }
- }
-
- /**
- * Inserts the js scripts/css in the head section
- * @param string &$output
- * @return void
- */
- public function renderHead(&$output)
- {
- $html = '';
- $version = '?v=' . CConfig::get('version');
-
- ///CDebug::d($this->_cssFiles);
- ///CDebug::d($this->_scriptFiles);
-
- foreach ($this->_cssFiles as $media => $cssFiles) {
- foreach ($cssFiles as $level => $cssFile) {
- $html .= CHtml::cssFile($cssFile . $version, $media);
- }
- }
- foreach ($this->_css as $css) {
- $html .= CHtml::css($css[0], $css[1]);
- }
-
- if ($this->enableJavaScript) {
- if (isset($this->_scriptFiles[self::POS_HEAD])) {
- foreach ($this->_scriptFiles[self::POS_HEAD] as $level => $scriptFile) {
- $html .= CHtml::scriptFile($scriptFile . $version);
- }
- }
- if (isset($this->_scripts[self::POS_HEAD])) {
- $html .= CHtml::script(implode("\n", $this->_scripts[self::POS_HEAD])) . "\n";
- }
- }
-
- if ($html !== '') {
- $count = 0;
- $output = preg_replace('/(
]*>|<\\/head\s*>)/is', '<%%%head%%%>$1', $output, 1, $count);
- if ($count) {
- $output = str_replace('<%%%head%%%>', $html, $output);
- } else {
- $output = $html . $output;
- }
- }
- }
-
- /**
- * Inserts the scripts at the beginning of the
- * @param string &$output
- * @return void
- */
- public function renderBodyBegin(&$output)
- {
- $html = '';
- if (isset($this->_scriptFiles[self::POS_BODY_BEGIN])) {
- foreach ($this->_scriptFiles[self::POS_BODY_BEGIN] as $level => $scriptFile) {
- $html .= CHtml::scriptFile($scriptFile);
- }
- }
- if (isset($this->_scripts[self::POS_BODY_BEGIN])) {
- $html .= CHtml::script(implode("\n", $this->_scripts[self::POS_BODY_BEGIN])) . "\n";
- }
-
- if ($html !== '') {
- $count = 0;
- $output = preg_replace('/(]*>)/is', '$1<%%%begin%%%>', $output, 1, $count);
- if ($count) {
- $output = str_replace('<%%%begin%%%>', $html, $output);
- } else {
- $output = $html . $output;
- }
- }
- }
-
- /**
- * Inserts the scripts at the end of the
- * @param string &$output
- * @return void
- */
- public function renderBodyEnd(&$output)
- {
- if (!isset($this->_scriptFiles[self::POS_BODY_END]) &&
- !isset($this->_scripts[self::POS_BODY_END]) &&
- !isset($this->_scripts[self::POS_JQUERY_READY]) &&
- !isset($this->_scripts[self::POS_DOC_READY]) &&
- !isset($this->_scripts[self::POS_ON_LOAD]))
- return;
-
- $completePage = 0;
- $output = preg_replace('/(<\\/body\s*>)/is', '<%%%end%%%>$1', $output, 1, $completePage);
- $html = '';
- if (isset($this->_scriptFiles[self::POS_BODY_END])) {
- foreach ($this->_scriptFiles[self::POS_BODY_END] as $level => $scriptFile) {
- $html .= CHtml::scriptFile($scriptFile);
- }
- }
-
- $scripts = isset($this->_scripts[self::POS_BODY_END]) ? $this->_scripts[self::POS_BODY_END] : array();
- if (isset($this->_scripts[self::POS_JQUERY_READY])) {
- if ($completePage) {
- $scripts[] = "jQuery(function($){\n" . implode("\n", $this->_scripts[self::POS_JQUERY_READY]) . "\n});";
- } else {
- $scripts[] = implode("\n", $this->_scripts[self::POS_JQUERY_READY]);
- }
- }
- if (isset($this->_scripts[self::POS_DOC_READY])) {
- if ($completePage) {
- $scripts[] = "jQuery(document).ready(function(){\n" . implode("\n", $this->_scripts[self::POS_DOC_READY]) . "\n});";
- } else {
- $scripts[] = implode("\n", $this->_scripts[self::POS_DOC_READY]);
- }
- }
- if (isset($this->_scripts[self::POS_ON_LOAD])) {
- if ($completePage) {
- $scripts[] = "jQuery(window).load(function(){\n" . implode("\n", $this->_scripts[self::POS_ON_LOAD]) . "\n});";
- } else {
- $scripts[] = implode("\n", $this->_scripts[self::POS_ON_LOAD]);
- }
- }
- if (!empty($scripts)) $html .= CHtml::script(implode("\n", $scripts)) . "\n";
-
- if ($completePage) {
- $output = str_replace('<%%%end%%%>', $html, $output);
- } else {
- $output = $output . $html;
- }
- }
-
- /**
- * Returns count of compressed files
- * @param string $type
- * @returm int
- */
- public function countCompressedFiles($type = 'css')
- {
- if ($type == 'css') {
- return $this->_countCompressedCssFiles;
- } elseif ($type == 'js') {
- return $this->_countCompressedJsFiles;
- }
-
- return 0;
- }
-
- /**
- * Prepare css files to compression
- * @returm bool
- */
- protected function _cssToCompression()
- {
- if (!CConfig::get('compression.css.enable')) {
- return false;
- }
-
- if (is_array($this->_cssFiles)) {
- $tempCssFiles = array();
- foreach ($this->_cssFiles as $media => $styleFiles) {
-
- $this->_countCompressedCssFiles = count($styleFiles);
- $cssFilesHash = md5(serialize($styleFiles));
- $minifiedDir = CConfig::get('compression.css.path');
- $minifyCss = CConfig::get('compression.css.minify.' . (A::app()->view->getTemplate() === 'backend' ? 'backend' : 'frontend'));
- $baseUrl = A::app()->getRequest()->getBaseUrl();
- $controller = A::app()->view->getController();
- $action = A::app()->view->getAction();
- $minifiedFile = md5($baseUrl . $controller . '/' . $action . '/' . $this->_countCompressedCssFiles . '/' . $cssFilesHash) . '.css';
- $cssMinifiedFile = $minifiedDir . $minifiedFile;
-
- // Fix - skip assets directory
- if (in_array(strtolower($controller), array('assets'))) {
- continue;
- }
-
- // Check if minified file exists and exit
- if (!empty($minifiedFile) && file_exists($minifiedDir . $minifiedFile) && (filesize($minifiedDir . $minifiedFile) > 0)) {
- $tempCssFiles[$media][] = $cssMinifiedFile;
- continue;
- }
-
- // Collect CSS files content
- $count = 0;
- $cssContent = '';
- $baseUrl = str_ireplace(array('http:', 'https:'), '', $baseUrl);
- foreach ($styleFiles as $key => $cssFile) {
- $fileContent = CFile::getFileContent($cssFile);
- if ($fileContent) {
- if ($minifyCss) {
- $fileContent = CMinify::css($fileContent);
- }
-
- // Replace relative paths with absolute
- $dirName = dirname($cssFile);
-
- // ../../ : ../../templates/default/css => //domain.com/templates/default/css
- $dirNameLevel2 = explode('/', $dirName);
- array_pop($dirNameLevel2);
- array_pop($dirNameLevel2);
- $dirNameLevel2 = $baseUrl . implode('/', $dirNameLevel2) . '/';
- $fileContent = str_ireplace(array("url('../../", "url(../../"), array("url('" . $dirNameLevel2, "url(" . $dirNameLevel2), $fileContent);
-
- // ../ : ../templates/default/css => //domain.com/templates/default/css
- $dirNameLevel1 = explode('/', $dirName);
- array_pop($dirNameLevel1);
- $dirNameLevel1 = $baseUrl . implode('/', $dirNameLevel1) . '/';
- $fileContent = str_ireplace(array("url('../", "url(../"), array("url('" . $dirNameLevel1, "url(" . $dirNameLevel1), $fileContent);
-
- // url('') : url('fonts/font.eot') => url('//domain.com/fonts/font.eot')
- $dirNameLevel0 = explode('/', $dirName);
- $dirNameLevel0 = $baseUrl . implode('/', $dirNameLevel0) . '/';
- $fileContent = str_ireplace(array("url('font", "url(font"), array("url('" . $dirNameLevel0 . "font", "url(" . $dirNameLevel0 . "font"), $fileContent);
-
- $cssContent .= "/* CSS File " . ++$count . ": " . $cssFile . " */" . PHP_EOL . $fileContent . PHP_EOL . PHP_EOL;
- }
- }
-
- if (!empty($cssContent)) {
- // Remove oldest file if the limit of minified is reached
- if (CFile::getDirectoryFilesCount($minifiedDir, '.css') >= self::CSS_MINIFY_LIMIT) {
- CFile::removeDirectoryOldestFile($minifiedDir, 0, array('index.html'));
- }
-
- CFile::writeToFile($minifiedDir . $minifiedFile, $cssContent);
- $tempCssFiles[$media][] = $cssMinifiedFile;
- }
- }
- $this->_cssFiles = $tempCssFiles;
- }
-
- return true;
- }
-
- /**
- * Prepare js files to compression
- * @returm bool
- */
- protected function _jsToCompression()
- {
- if (!CConfig::get('compression.js.enable')) {
- return false;
- }
-
- if (is_array($this->_scriptFiles)) {
- $tempJsFiles = array();
- foreach ($this->_scriptFiles as $position => $scriptFiles) {
-
- $countCompressedJsFiles = count($scriptFiles);
- $this->_countCompressedJsFiles += $countCompressedJsFiles;
- $jsFilesHash = md5(serialize($scriptFiles));
- $minifiedDir = CConfig::get('compression.js.path');
- $minifyJs = CConfig::get('compression.js.minify.' . (A::app()->view->getTemplate() === 'backend' ? 'backend' : 'frontend'));
- $baseUrl = A::app()->getRequest()->getBaseUrl();
- $controller = A::app()->view->getController();
- $action = A::app()->view->getAction();
-
- $minifiedFile = md5($baseUrl . $controller . '/' . $action . '/' . $position . '/' . $countCompressedJsFiles . '/' . $jsFilesHash) . '.js';
- $jsMinifiedFile = $minifiedDir . $minifiedFile;
-
- // Fix - skip assets directory
- if (in_array(strtolower($controller), array('assets'))) {
- continue;
- }
-
- // Check if minified file exists and exit
- if (!empty($minifiedFile) && file_exists($minifiedDir . $minifiedFile) && (filesize($minifiedDir . $minifiedFile) > 0)) {
- $tempJsFiles[$position][] = $jsMinifiedFile;
- continue;
- }
-
- // Collect JS files content
- $count = 0;
- $jsContent = '';
- $baseUrl = str_ireplace(array('http:', 'https:'), '', $baseUrl);
- foreach ($scriptFiles as $key => $url) {
- $fileContent = CFile::getFileContent($url);
- if ($fileContent) {
- if ($minifyJs) {
- $fileContent = CMinify::js($fileContent);
- }
- $jsContent .= "/* JS File " . ++$count . ": " . $url . " */" . PHP_EOL . $fileContent . PHP_EOL . PHP_EOL;
- }
- }
-
- if (!empty($jsContent)) {
- // Remove oldest file if the limit of minified is reached
- if (CFile::getDirectoryFilesCount($minifiedDir, '.js') >= self::JS_MINIFY_LIMIT) {
- CFile::removeDirectoryOldestFile($minifiedDir, 0, array('index.html'));
- }
-
- CFile::writeToFile($minifiedDir . $minifiedFile, $jsContent);
- $tempJsFiles[$position][] = $jsMinifiedFile;
- }
- }
- $this->_scriptFiles = $tempJsFiles;
- }
-
- return true;
- }
-
- /**
- * Returns CSS or JS array of files, sorted by level
- * @param array &$filesArray
- * @returm voies
- */
- private function _sortByLevel(&$filesArray = array())
- {
- foreach ($filesArray as $key => $files) {
- ksort($files);
- $filesArray[$key] = $files;
- }
- }
-
+ /** The script is rendered in the */
+ const POS_HEAD = 0;
+ /** The script is rendered at the beginning of the */
+ const POS_BODY_BEGIN = 1;
+ /** The script is rendered at the end of the */
+ const POS_BODY_END = 2;
+ /** The script is rendered inside window onload function */
+ const POS_ON_LOAD = 3;
+ /** The body script is rendered inside a jQuery ready function */
+ const POS_JQUERY_READY = 4;
+ /** The script is rendered inside document ready function */
+ const POS_DOC_READY = 5;
+
+ /** The limit in amount of css minify files */
+ const CSS_MINIFY_LIMIT = 100;
+ /** The limit in amount of js minify files */
+ const JS_MINIFY_LIMIT = 100;
+
+ /** @var boolean */
+ public $enableJavaScript = true;
+ /** @var array */
+ protected $_cssFiles = [];
+ /** @var int */
+ protected $_countCompressedCssFiles = 0;
+ /** @var array */
+ protected $_css = [];
+ /** @var array */
+ protected $_scriptFiles = [];
+ /** @var int */
+ protected $_countCompressedJsFiles = 0;
+ /** @var array */
+ protected $_scripts = [];
+ /** @var boolean */
+ protected $_hasStyles = false;
+ /** @var boolean */
+ protected $_hasScripts = false;
+ /** @var string */
+ protected $_countJsFiles = 0;
+ /** @var string */
+ protected $_countCssFiles = 0;
+
+
+ /**
+ * Class default constructor
+ */
+ function __construct()
+ {
+ }
+
+ /**
+ * Returns the instance of object
+ *
+ * @return current class
+ */
+ public static function init()
+ {
+ return parent::init(__CLASS__);
+ }
+
+ /**
+ * Registers a piece of CSS code
+ *
+ * @param string $id
+ * @param string $css
+ * @param string $media
+ *
+ * @return void
+ */
+ public function registerCss($id, $css, $media = '')
+ {
+ if ( ! empty($css)) {
+ if ( ! $this->_hasStyles) {
+ $this->_hasStyles = true;
+ }
+ $this->_css[$id] = [$css, $media];
+ }
+ }
+
+ /**
+ * Registers CSS file
+ *
+ * @param string $url
+ * @param string $media
+ * @param string $level
+ *
+ * @return void
+ */
+ public function registerCssFile($url, $media = 'all', $level = '')
+ {
+ if ( ! empty($url)) {
+ if ( ! $this->_hasStyles) {
+ $this->_hasStyles = true;
+ }
+ if ( ! isset($this->_cssFiles[$media])) {
+ $this->_cssFiles[$media] = [];
+ }
+ if ( ! in_array($url, $this->_cssFiles[$media])) {
+ if ( ! empty($level)) {
+ if (isset($this->_cssFiles[$media][$level])) {
+ CDebug::addMessage(
+ 'warnings',
+ 'css-file-exists',
+ A::t(
+ 'core',
+ 'CSS file with the same level "{level}" already registered: "{file}"',
+ ['{level}' => $level, '{file}' => $url]
+ )
+ );
+ }
+ $this->_cssFiles[$media][$level] = $url;
+ } else {
+ $this->_cssFiles[$media][$this->_countCssFiles++] = $url;
+ }
+ }
+ }
+ }
+
+ /**
+ * Registers CSS files
+ *
+ * @param string $urls
+ * @param string $path
+ *
+ * @return void
+ */
+ public function registerCssFiles($urls, $path = '')
+ {
+ if ( ! empty($urls) && [$urls]) {
+ foreach ($urls as $key => $url) {
+ if (empty($url)) {
+ continue;
+ }
+ $path = ! empty($path) ? trim($path, '/').'/' : '';
+ $url = $path.(is_array($url) ? $key : $url);
+ $media = (is_array($url) && ! empty($url['media'])) ? ' media="'.$url['media'].'"' : 'all';
+ $this->registerCssFile($url, $media);
+ }
+ }
+ }
+
+ /**
+ * Registers a piece of javascript code
+ *
+ * @param string $id
+ * @param string $script
+ * @param integer $position
+ *
+ * @return void
+ */
+ public function registerScript($id, $script, $position = self::POS_JQUERY_READY)
+ {
+ if ( ! empty($script)) {
+ if ( ! $this->_hasScripts) {
+ $this->_hasScripts = true;
+ }
+ $this->_scripts[$position][$id] = $script;
+ if ($position === self::POS_JQUERY_READY || $position === self::POS_ON_LOAD) {
+ $this->registerCoreScript('jquery');
+ }
+ }
+ }
+
+ /**
+ * Registers javascript file
+ *
+ * @param string $url
+ * @param integer $position self::POS_HEAD|self::POS_BODY_BEGIN|self::POS_BODY_END
+ * @param string $level
+ *
+ * @return void
+ */
+ public function registerScriptFile($url, $position = self::POS_HEAD, $level = '')
+ {
+ if ( ! empty($url)) {
+ if ( ! $this->_hasScripts) {
+ $this->_hasScripts = true;
+ }
+ if ( ! isset($this->_scriptFiles[$position])) {
+ $this->_scriptFiles[$position] = [];
+ }
+ if ( ! in_array($url, $this->_scriptFiles[$position])) {
+ if ( ! empty($level)) {
+ if (isset($this->_scriptFiles[$position][$level])) {
+ CDebug::addMessage(
+ 'warnings',
+ 'js-file-exists',
+ A::t(
+ 'core',
+ 'Javascript file with the same level "{level}" already registered: "{file}"',
+ ['{level}' => $level, '{file}' => $url]
+ )
+ );
+ }
+ $this->_scriptFiles[$position][$level] = $url;
+ } else {
+ $this->_scriptFiles[$position][$this->_countJsFiles++] = $url;
+ }
+ }
+ }
+ }
+
+ /**
+ * Registers a required javascript file
+ *
+ * @param string $url
+ * @param string $path
+ * @param integer $position self::POS_HEAD|self::POS_BODY_BEGIN|self::POS_BODY_END
+ *
+ * @return void
+ */
+ public function registerScriptFiles($urls, $path = '', $position = self::POS_HEAD)
+ {
+ if ( ! empty($urls) && [$urls]) {
+ foreach ($urls as $key => $url) {
+ $path = ! empty($path) ? trim($path, '/').'/' : '';
+ $url = $path.(is_array($url) ? $key : $url);
+ $position = (is_array($url) && ! empty($url['position'])) ? $url['position'] : $position;
+ $this->registerScriptFile($url, $position);
+ }
+ }
+ }
+
+ /**
+ * Registers a core script package
+ *
+ * @param string $name
+ *
+ * @return void
+ */
+ public function registerCoreScript($name)
+ {
+ // Registers core script
+ }
+
+ /**
+ * Renders the registered scripts in our class
+ * This method is called in View->render() class
+ *
+ * @param string &$output
+ *
+ * @return void|bool
+ */
+ public function render(&$output)
+ {
+ if ( ! $this->_hasStyles && ! $this->_hasScripts) {
+ return false;
+ }
+
+ // Sort CSS according by level
+ $this->_sortByLevel($this->_cssFiles);
+
+ // Sort JS scripts by level
+ if ($this->enableJavaScript) {
+ $this->_sortByLevel($this->_scriptFiles);
+ }
+
+ $this->_cssToCompression();
+ $this->_jsToCompression();
+ $this->renderHead($output);
+ if ($this->enableJavaScript) {
+ $this->renderBodyBegin($output);
+ $this->renderBodyEnd($output);
+ }
+ }
+
+ /**
+ * Inserts the js scripts/css in the head section
+ *
+ * @param string &$output
+ *
+ * @return void
+ */
+ public function renderHead(&$output)
+ {
+ $html = '';
+ $version = '?v='.CConfig::get('version');
+
+ ///CDebug::d($this->_cssFiles);
+ ///CDebug::d($this->_scriptFiles);
+
+ foreach ($this->_cssFiles as $media => $cssFiles) {
+ foreach ($cssFiles as $level => $cssFile) {
+ $html .= CHtml::cssFile($cssFile.$version, $media);
+ }
+ }
+ foreach ($this->_css as $css) {
+ $html .= CHtml::css($css[0], $css[1]);
+ }
+
+ if ($this->enableJavaScript) {
+ if (isset($this->_scriptFiles[self::POS_HEAD])) {
+ foreach ($this->_scriptFiles[self::POS_HEAD] as $level => $scriptFile) {
+ $html .= CHtml::scriptFile($scriptFile.$version);
+ }
+ }
+ if (isset($this->_scripts[self::POS_HEAD])) {
+ $html .= CHtml::script(implode("\n", $this->_scripts[self::POS_HEAD]))."\n";
+ }
+ }
+
+ if ($html !== '') {
+ $count = 0;
+ $output = preg_replace('/(
]*>|<\\/head\s*>)/is', '<%%%head%%%>$1', $output, 1, $count);
+ if ($count) {
+ $output = str_replace('<%%%head%%%>', $html, $output);
+ } else {
+ $output = $html.$output;
+ }
+ }
+ }
+
+ /**
+ * Inserts the scripts at the beginning of the
+ *
+ * @param string &$output
+ *
+ * @return void
+ */
+ public function renderBodyBegin(&$output)
+ {
+ $html = '';
+ if (isset($this->_scriptFiles[self::POS_BODY_BEGIN])) {
+ foreach ($this->_scriptFiles[self::POS_BODY_BEGIN] as $level => $scriptFile) {
+ $html .= CHtml::scriptFile($scriptFile);
+ }
+ }
+ if (isset($this->_scripts[self::POS_BODY_BEGIN])) {
+ $html .= CHtml::script(implode("\n", $this->_scripts[self::POS_BODY_BEGIN]))."\n";
+ }
+
+ if ($html !== '') {
+ $count = 0;
+ $output = preg_replace('/(]*>)/is', '$1<%%%begin%%%>', $output, 1, $count);
+ if ($count) {
+ $output = str_replace('<%%%begin%%%>', $html, $output);
+ } else {
+ $output = $html.$output;
+ }
+ }
+ }
+
+ /**
+ * Inserts the scripts at the end of the
+ *
+ * @param string &$output
+ *
+ * @return void
+ */
+ public function renderBodyEnd(&$output)
+ {
+ if ( ! isset($this->_scriptFiles[self::POS_BODY_END])
+ &&
+ ! isset($this->_scripts[self::POS_BODY_END])
+ &&
+ ! isset($this->_scripts[self::POS_JQUERY_READY])
+ &&
+ ! isset($this->_scripts[self::POS_DOC_READY])
+ &&
+ ! isset($this->_scripts[self::POS_ON_LOAD])
+ ) {
+ return;
+ }
+
+ $completePage = 0;
+ $output = preg_replace('/(<\\/body\s*>)/is', '<%%%end%%%>$1', $output, 1, $completePage);
+ $html = '';
+ if (isset($this->_scriptFiles[self::POS_BODY_END])) {
+ foreach ($this->_scriptFiles[self::POS_BODY_END] as $level => $scriptFile) {
+ $html .= CHtml::scriptFile($scriptFile);
+ }
+ }
+
+ $scripts = isset($this->_scripts[self::POS_BODY_END]) ? $this->_scripts[self::POS_BODY_END] : [];
+ if (isset($this->_scripts[self::POS_JQUERY_READY])) {
+ if ($completePage) {
+ $scripts[] = "jQuery(function($){\n".implode("\n", $this->_scripts[self::POS_JQUERY_READY])."\n});";
+ } else {
+ $scripts[] = implode("\n", $this->_scripts[self::POS_JQUERY_READY]);
+ }
+ }
+ if (isset($this->_scripts[self::POS_DOC_READY])) {
+ if ($completePage) {
+ $scripts[] = "jQuery(document).ready(function(){\n".implode("\n", $this->_scripts[self::POS_DOC_READY])
+ ."\n});";
+ } else {
+ $scripts[] = implode("\n", $this->_scripts[self::POS_DOC_READY]);
+ }
+ }
+ if (isset($this->_scripts[self::POS_ON_LOAD])) {
+ if ($completePage) {
+ $scripts[] = "jQuery(window).load(function(){\n".implode("\n", $this->_scripts[self::POS_ON_LOAD])
+ ."\n});";
+ } else {
+ $scripts[] = implode("\n", $this->_scripts[self::POS_ON_LOAD]);
+ }
+ }
+ if ( ! empty($scripts)) {
+ $html .= CHtml::script(implode("\n", $scripts))."\n";
+ }
+
+ if ($completePage) {
+ $output = str_replace('<%%%end%%%>', $html, $output);
+ } else {
+ $output = $output.$html;
+ }
+ }
+
+ /**
+ * Returns count of compressed files
+ *
+ * @param string $type
+ *
+ * @returm int
+ */
+ public function countCompressedFiles($type = 'css')
+ {
+ if ($type == 'css') {
+ return $this->_countCompressedCssFiles;
+ } elseif ($type == 'js') {
+ return $this->_countCompressedJsFiles;
+ }
+
+ return 0;
+ }
+
+ /**
+ * Prepare css files to compression
+ *
+ * @returm bool
+ */
+ protected function _cssToCompression()
+ {
+ if ( ! CConfig::get('compression.css.enable')) {
+ return false;
+ }
+
+ if (is_array($this->_cssFiles)) {
+ $tempCssFiles = [];
+ foreach ($this->_cssFiles as $media => $styleFiles) {
+ $this->_countCompressedCssFiles = count($styleFiles);
+ $cssFilesHash = md5(serialize($styleFiles));
+ $minifiedDir = CConfig::get('compression.css.path');
+ $minifyCss = CConfig::get(
+ 'compression.css.minify.'.(A::app()->view->getTemplate() === 'backend' ? 'backend' : 'frontend')
+ );
+ $baseUrl = A::app()->getRequest()->getBaseUrl();
+ $controller = A::app()->view->getController();
+ $action = A::app()->view->getAction();
+ $minifiedFile = md5(
+ $baseUrl.$controller.'/'.$action.'/'.$this->_countCompressedCssFiles.'/'.$cssFilesHash
+ ).'.css';
+ $cssMinifiedFile = $minifiedDir.$minifiedFile;
+
+ // Fix - skip assets directory
+ if (in_array(strtolower($controller), ['assets'])) {
+ continue;
+ }
+
+ // Check if minified file exists and exit
+ if ( ! empty($minifiedFile) && file_exists($minifiedDir.$minifiedFile)
+ && (filesize(
+ $minifiedDir.$minifiedFile
+ ) > 0)
+ ) {
+ $tempCssFiles[$media][] = $cssMinifiedFile;
+ continue;
+ }
+
+ // Collect CSS files content
+ $count = 0;
+ $cssContent = '';
+ $baseUrl = str_ireplace(['http:', 'https:'], '', $baseUrl);
+ foreach ($styleFiles as $key => $cssFile) {
+ $fileContent = CFile::getFileContent($cssFile);
+ if ($fileContent) {
+ if ($minifyCss) {
+ $fileContent = CMinify::css($fileContent);
+ }
+
+ // Replace relative paths with absolute
+ $dirName = dirname($cssFile);
+
+ // ../../ : ../../templates/default/css => //domain.com/templates/default/css
+ $dirNameLevel2 = explode('/', $dirName);
+ array_pop($dirNameLevel2);
+ array_pop($dirNameLevel2);
+ $dirNameLevel2 = $baseUrl.implode('/', $dirNameLevel2).'/';
+ $fileContent = str_ireplace(
+ ["url('../../", "url(../../"],
+ ["url('".$dirNameLevel2, "url(".$dirNameLevel2],
+ $fileContent
+ );
+
+ // ../ : ../templates/default/css => //domain.com/templates/default/css
+ $dirNameLevel1 = explode('/', $dirName);
+ array_pop($dirNameLevel1);
+ $dirNameLevel1 = $baseUrl.implode('/', $dirNameLevel1).'/';
+ $fileContent = str_ireplace(
+ ["url('../", "url(../"],
+ ["url('".$dirNameLevel1, "url(".$dirNameLevel1],
+ $fileContent
+ );
+
+ // url('') : url('fonts/font.eot') => url('//domain.com/fonts/font.eot')
+ $dirNameLevel0 = explode('/', $dirName);
+ $dirNameLevel0 = $baseUrl.implode('/', $dirNameLevel0).'/';
+ $fileContent = str_ireplace(
+ ["url('font", "url(font"],
+ ["url('".$dirNameLevel0."font", "url(".$dirNameLevel0."font"],
+ $fileContent
+ );
+
+ $cssContent .= "/* CSS File ".++$count.": ".$cssFile." */".PHP_EOL.$fileContent.PHP_EOL.PHP_EOL;
+ }
+ }
+
+ if ( ! empty($cssContent)) {
+ // Remove oldest file if the limit of minified is reached
+ if (CFile::getDirectoryFilesCount($minifiedDir, '.css') >= self::CSS_MINIFY_LIMIT) {
+ CFile::removeDirectoryOldestFile($minifiedDir, 0, ['index.html']);
+ }
+
+ CFile::writeToFile($minifiedDir.$minifiedFile, $cssContent);
+ $tempCssFiles[$media][] = $cssMinifiedFile;
+ }
+ }
+ $this->_cssFiles = $tempCssFiles;
+ }
+
+ return true;
+ }
+
+ /**
+ * Prepare js files to compression
+ *
+ * @returm bool
+ */
+ protected function _jsToCompression()
+ {
+ if ( ! CConfig::get('compression.js.enable')) {
+ return false;
+ }
+
+ if (is_array($this->_scriptFiles)) {
+ $tempJsFiles = [];
+ foreach ($this->_scriptFiles as $position => $scriptFiles) {
+ $countCompressedJsFiles = count($scriptFiles);
+ $this->_countCompressedJsFiles += $countCompressedJsFiles;
+ $jsFilesHash = md5(serialize($scriptFiles));
+ $minifiedDir = CConfig::get('compression.js.path');
+ $minifyJs = CConfig::get(
+ 'compression.js.minify.'.(A::app()->view->getTemplate() === 'backend' ? 'backend' : 'frontend')
+ );
+ $baseUrl = A::app()->getRequest()->getBaseUrl();
+ $controller = A::app()->view->getController();
+ $action = A::app()->view->getAction();
+
+ $minifiedFile = md5(
+ $baseUrl.$controller.'/'.$action.'/'.$position.'/'.$countCompressedJsFiles.'/'.$jsFilesHash
+ ).'.js';
+ $jsMinifiedFile = $minifiedDir.$minifiedFile;
+
+ // Fix - skip assets directory
+ if (in_array(strtolower($controller), ['assets'])) {
+ continue;
+ }
+
+ // Check if minified file exists and exit
+ if ( ! empty($minifiedFile) && file_exists($minifiedDir.$minifiedFile)
+ && (filesize(
+ $minifiedDir.$minifiedFile
+ ) > 0)
+ ) {
+ $tempJsFiles[$position][] = $jsMinifiedFile;
+ continue;
+ }
+
+ // Collect JS files content
+ $count = 0;
+ $jsContent = '';
+ $baseUrl = str_ireplace(['http:', 'https:'], '', $baseUrl);
+ foreach ($scriptFiles as $key => $url) {
+ $fileContent = CFile::getFileContent($url);
+ if ($fileContent) {
+ if ($minifyJs) {
+ $fileContent = CMinify::js($fileContent);
+ }
+ $jsContent .= "/* JS File ".++$count.": ".$url." */".PHP_EOL.$fileContent.PHP_EOL.PHP_EOL;
+ }
+ }
+
+ if ( ! empty($jsContent)) {
+ // Remove oldest file if the limit of minified is reached
+ if (CFile::getDirectoryFilesCount($minifiedDir, '.js') >= self::JS_MINIFY_LIMIT) {
+ CFile::removeDirectoryOldestFile($minifiedDir, 0, ['index.html']);
+ }
+
+ CFile::writeToFile($minifiedDir.$minifiedFile, $jsContent);
+ $tempJsFiles[$position][] = $jsMinifiedFile;
+ }
+ }
+ $this->_scriptFiles = $tempJsFiles;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns CSS or JS array of files, sorted by level
+ *
+ * @param array &$filesArray
+ *
+ * @returm void
+ */
+ private function _sortByLevel(&$filesArray = [])
+ {
+ foreach ($filesArray as $key => $files) {
+ ksort($files);
+ $filesArray[$key] = $files;
+ }
+ }
+
}
\ No newline at end of file
diff --git a/framework/components/CComponent.php b/framework/components/CComponent.php
index c8d2b4e..b0174cd 100644
--- a/framework/components/CComponent.php
+++ b/framework/components/CComponent.php
@@ -17,54 +17,57 @@
class CComponent
{
-
- /* class name => component */
- private static $_components = array();
-
- /**
- * Class constructor
- * @return void
- */
- function __construct()
- {
-
- }
-
- /**
- * 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) == 'init') {
- if (count($args) == 1) {
- return self::_parentInit($args[0]);
- }
- }
- }
-
- /**
- * Returns the static component of the specified class
- * @param string $className
- *
- * EVERY derived component class must override this method in following way,
- *
- * public static function init()
- * {
- * return parent::init(__CLASS__);
- * }
- *
- */
- private static function _parentInit($className = __CLASS__)
- {
- if (isset(self::$_components[$className])) {
- return self::$_components[$className];
- } else {
- return self::$_components[$className] = new $className(null);
- }
- }
-
+
+ /* class name => component */
+ private static $_components = [];
+
+ /**
+ * Class constructor
+ *
+ * @return void
+ */
+ function __construct()
+ {
+ }
+
+ /**
+ * 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) == 'init') {
+ if (count($args) == 1) {
+ return self::_parentInit($args[0]);
+ }
+ }
+ }
+
+ /**
+ * Returns the static component of the specified class
+ *
+ * @param string $className
+ *
+ * EVERY derived component class must override this method in following way,
+ *
+ * public static function init()
+ * {
+ * return parent::init(__CLASS__);
+ * }
+ *
+ */
+ private static function _parentInit($className = __CLASS__)
+ {
+ if (isset(self::$_components[$className])) {
+ return self::$_components[$className];
+ } else {
+ return self::$_components[$className] = new $className(null);
+ }
+ }
+
}
\ No newline at end of file
diff --git a/framework/components/CDbHttpSession.php b/framework/components/CDbHttpSession.php
index 19c06cf..45fc208 100644
--- a/framework/components/CDbHttpSession.php
+++ b/framework/components/CDbHttpSession.php
@@ -39,402 +39,448 @@
class CDbHttpSession extends CComponent
{
-
- /** @var boolean */
- protected $_autoStart = true;
- /** @var string */
- protected $_defaultSessionName = 'apphp_framework';
- /** @var string */
- protected $_defaultSessionPrefix = 'apphp_';
- /**
- * @var int
- * @deprecated since v0.1.0
- * 0 - use name prefix, 1 - use session name (default)
- */
- protected $_multiSiteSupportType = 1;
- /**
- * @var mixed
- */
- protected $_prefix = '';
-
- /** @var Database */
- private $_db;
-
-
- /**
- * Class default constructor
- */
- function __construct()
- {
- $this->_db = CDatabase::init();
-
- @session_set_save_handler(
- array($this, 'openSession'),
- array($this, 'closeSession'),
- array($this, 'readSession'),
- array($this, 'writeSession'),
- array($this, 'destroySession'),
- array($this, 'gcSession')
- );
-
- // The following prevents unexpected effects when using objects as save handlers
- register_shutdown_function('session_write_close');
-
- if ($this->_multiSiteSupportType) {
- $this->setSessionName('apphp_' . CConfig::get('installationKey'));
- } else {
- $this->setSessionPrefix('apphp_' . CConfig::get('installationKey'));
- }
-
- if ($this->_autoStart) $this->_startSession();
- }
-
- /**
- * Returns the instance of object
- * @return current class
- */
- public static function init()
- {
- return parent::init(__CLASS__);
- }
-
- /**
- * Sets session variable
- * @param string $name
- * @param mixed $value
- */
- public function set($name, $value)
- {
- $_SESSION[$name] = $value;
- }
-
- /**
- * Returns session variable
- * @param string $name
- * @param mixed $default
- */
- public function get($name, $default = '')
- {
- return isset($_SESSION[$name]) ? $_SESSION[$name] : $default;
- }
-
- /**
- * Removes session variable
- * @param string $name
- */
- public function remove($name)
- {
- if (isset($_SESSION[$name])) {
- unset($_SESSION[$name]);
- return true;
- }
-
- return false;
- }
-
- /**
- * Removes all session variable
- * @return void
- */
- public function removeAll()
- {
- @session_unset();
- if (is_array($_SESSION)) {
- foreach ($_SESSION as $key => $val) {
- unset($_SESSION[$key]);
- }
- }
- }
-
- /**
- * Checks if session variable exists
- * @param string $name
- */
- public function isExists($name)
- {
- return isset($_SESSION[$name]) ? true : false;
- }
-
- /**
- * Sets session flash data
- * @param string $name
- * @param mixed $value
- */
- public function setFlash($name, $value)
- {
- $_SESSION[$this->_prefix . '_flash'][$name] = $value;
- }
-
- /**
- * Returns session flash data
- * @param string $name
- * @param mixed $default
- */
- public function getFlash($name, $default = '')
- {
- if (isset($_SESSION[$this->_prefix . '_flash'][$name])) {
- $result = $_SESSION[$this->_prefix . '_flash'][$name];
- unset($_SESSION[$this->_prefix . '_flash'][$name]);
- } else {
- $result = $default;
- }
- return $result;
- }
-
- /**
- * Checks if has flash data
- * @param string $name
- * @return bool
- */
- public function hasFlash($name)
- {
- return isset($_SESSION[$this->_prefix . '_flash'][$name]) ? true : false;
- }
-
- /**
- * Sets session name
- * @param string $value
- */
- public function setSessionName($value)
- {
- if (empty($value)) $value = $this->_defaultSessionName;
- session_name($value);
- }
-
- /**
- * Sets session name
- * @param string $value
- */
- public function setSessionPrefix($value)
- {
- if (empty($value)) $value = $this->_defaultSessionPrefix;
- $this->_prefix = $value;
- }
-
- /**
- * Gets session name
- * @return string
- */
- public function getSessionName()
- {
- return session_name();
- }
-
- /**
- * Destroys the session
- */
- public function endSession()
- {
- if (session_id() !== '') {
- @session_unset();
- @session_destroy();
- }
- }
-
- /**
- * Gets cookie mode
- * @return string
- */
- public function getCookieMode()
- {
- if (ini_get('session.use_cookies') === '0') {
- return 'none';
- } elseif (ini_get('session.use_only_cookies') === '0') {
- return 'allow';
- } else {
- return 'only';
- }
- }
-
- /**
- * Session open handler
- * Do not call this method directly
- * @param string $savePath
- * @param string $sessionName
- * @return boolean
- */
- public function openSession($savePath, $sessionName)
- {
- $this->_deleteExpiredSessions();
- return true;
- }
-
- /**
- * Session close handler
- * Do not call this method directly
- * @return boolean
- */
- public function closeSession()
- {
- if (session_id() !== '') @session_write_close();
- return true;
- }
-
- /**
- * Session read handler
- * Do not call this method directly
- * @param string $id
- * @return bool
- */
- public function readSession($id)
- {
- $result = $this->_db->select('SELECT session_data FROM `' . CConfig::get('db.prefix') . 'sessions` WHERE session_id = :session_id', array(':session_id' => $id));
-
- // Read session data and store it into $_SESSION array
- if (isset($result[0]['session_data'])) {
-
- $dataPairs = explode('|', $result[0]['session_data']);
-
- // Prepare array of session variables in the following format:
- // [var_name_1] => serialized data
- // [var_name_2] => serialized data
- // etc.
- $previousData = '';
- $previousName = '';
- $dataPairsNew = array();
- foreach ($dataPairs as $key => $val) {
- if (!empty($previousData)) {
-
- $previousDataRev = strrev($previousData);
- $po1 = strpos($previousDataRev, ';');
- $po2 = strpos($previousDataRev, '}');
-
- if ((!empty($po1) && empty($po2)) || (!empty($po1) && !empty($po2) && $po1 < $po2)) {
- $divider = ';';
- } else {
- $divider = '}';
- }
-
- $previousDataParts = explode($divider, $previousData);
- $previousDataCount = count($previousDataParts);
- $paramName = isset($previousDataParts[$previousDataCount - 1]) ? $previousDataParts[$previousDataCount - 1] : '';
- unset($previousDataParts[$previousDataCount - 1]);
- $paramValue = implode($divider, $previousDataParts);
-
- $dataPairsNew[$previousName] = $paramValue;
- if ($paramValue[0] == 'a') {
- $dataPairsNew[$previousName] .= '}';
- }
- } else {
- $paramName = $val;
- }
- $previousName = $paramName;
- $previousData = $val;
- }
-
- $dataPairsNew[$previousName] = $dataPairs[count($dataPairs) - 1];
-
- // Store session variables in global array $_SESSION
- foreach ($dataPairsNew as $key => $val) {
- if (!empty($key)) {
- $_SESSION[$key] = unserialize($val);
- }
- }
- }
-
- return true;
- }
-
- /**
- * Session write handler
- * Do not call this method directly
- * @param string $id
- * @param string $data
- * @return boolean
- */
- public function writeSession($id, $data)
- {
- $result = $this->_db->select('SELECT * from `' . CConfig::get('db.prefix') . 'sessions` WHERE session_id = :session_id', array(':session_id' => $id));
- if (isset($result[0])) {
- $result = $this->_db->update(
- 'sessions',
- array(
- 'expires_at' => time() + $this->getTimeout(),
- 'session_data' => $data,
- ),
- 'session_id = :session_id',
- array(':session_id' => $id)
- );
- } else {
- $result = $this->_db->insert(
- 'sessions',
- array(
- 'session_id' => $id,
- 'expires_at' => time() + $this->getTimeout(),
- 'session_data' => $data,
- )
- );
- }
-
- return true;
- }
-
- /**
- * Session destroy handler
- * Do not call this method directly
- * @param string $id
- * @return boolean
- */
- public function destroySession($id)
- {
- return $this->_db->delete('sessions', "session_id = :session_id", array(':session_id' => $id));
- }
-
- /**
- * Session garbage collection handler
- * Do not call this method directly
- * @param int $maxLifetime
- * @return boolean
- */
- public function gcSession($maxLifetime)
- {
- return $this->_deleteExpiredSessions();
- }
-
- /**
- * Sets the number of seconds after which data will be seen as 'garbage' and cleaned up
- * @param int $value
- */
- public function setTimeout($value)
- {
- ini_set('session.gc_maxlifetime', (int)$value);
- }
-
- /**
- * Returns the number of seconds after which data will be seen as 'garbage' and cleaned up
- * @return integer
- */
- public function getTimeout()
- {
- // Get lifetime value from configuration file (in minutes)
- $maxlifetime = CConfig::get('session.lifetime');
- return (!empty($maxlifetime)) ? (int)($maxlifetime * 60) : (int)ini_get('session.gc_maxlifetime');
- }
-
- /**
- * Starts the session if it has not started yet
- */
- private function _startSession()
- {
- // Set lifetime value from configuration file (in minutes)
- $maxLifetime = CConfig::get('session.lifetime');
- if (!empty($maxLifetime) && $maxLifetime != ini_get('session.gc_maxlifetime')) {
- $this->setTimeout($maxLifetime);
- }
-
- @session_start();
- if (APPHP_MODE == 'debug' && session_id() == '') {
- CDebug::addMessage('errors', 'session', A::t('core', 'Failed to start session'));
- }
- }
-
- /**
- * Deletes expired sessions
- * @return bool
- */
- private function _deleteExpiredSessions()
- {
- return $this->_db->delete('sessions', 'expires_at < :expires_at', array(':expires_at' => time()));
- }
-
+
+ /** @var boolean */
+ protected $_autoStart = true;
+ /** @var string */
+ protected $_defaultSessionName = 'apphp_framework';
+ /** @var string */
+ protected $_defaultSessionPrefix = 'apphp_';
+ /**
+ * @var int
+ * @deprecated since v0.1.0
+ * 0 - use name prefix, 1 - use session name (default)
+ */
+ protected $_multiSiteSupportType = 1;
+ /**
+ * @var mixed
+ */
+ protected $_prefix = '';
+
+ /** @var Database */
+ private $_db;
+
+
+ /**
+ * Class default constructor
+ */
+ function __construct()
+ {
+ $this->_db = CDatabase::init();
+
+ @session_set_save_handler(
+ [$this, 'openSession'],
+ [$this, 'closeSession'],
+ [$this, 'readSession'],
+ [$this, 'writeSession'],
+ [$this, 'destroySession'],
+ [$this, 'gcSession']
+ );
+
+ // The following prevents unexpected effects when using objects as save handlers
+ register_shutdown_function('session_write_close');
+
+ if ($this->_multiSiteSupportType) {
+ $this->setSessionName('apphp_'.CConfig::get('installationKey'));
+ } else {
+ $this->setSessionPrefix('apphp_'.CConfig::get('installationKey'));
+ }
+
+ if ($this->_autoStart) {
+ $this->_startSession();
+ }
+ }
+
+ /**
+ * Returns the instance of object
+ *
+ * @return current class
+ */
+ public static function init()
+ {
+ return parent::init(__CLASS__);
+ }
+
+ /**
+ * Sets session variable
+ *
+ * @param string $name
+ * @param mixed $value
+ */
+ public function set($name, $value)
+ {
+ $_SESSION[$name] = $value;
+ }
+
+ /**
+ * Returns session variable
+ *
+ * @param string $name
+ * @param mixed $default
+ */
+ public function get($name, $default = '')
+ {
+ return isset($_SESSION[$name]) ? $_SESSION[$name] : $default;
+ }
+
+ /**
+ * Removes session variable
+ *
+ * @param string $name
+ */
+ public function remove($name)
+ {
+ if (isset($_SESSION[$name])) {
+ unset($_SESSION[$name]);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Removes all session variable
+ *
+ * @return void
+ */
+ public function removeAll()
+ {
+ @session_unset();
+ if (is_array($_SESSION)) {
+ foreach ($_SESSION as $key => $val) {
+ unset($_SESSION[$key]);
+ }
+ }
+ }
+
+ /**
+ * Checks if session variable exists
+ *
+ * @param string $name
+ */
+ public function isExists($name)
+ {
+ return isset($_SESSION[$name]) ? true : false;
+ }
+
+ /**
+ * Sets session flash data
+ *
+ * @param string $name
+ * @param mixed $value
+ */
+ public function setFlash($name, $value)
+ {
+ $_SESSION[$this->_prefix.'_flash'][$name] = $value;
+ }
+
+ /**
+ * Returns session flash data
+ *
+ * @param string $name
+ * @param mixed $default
+ */
+ public function getFlash($name, $default = '')
+ {
+ if (isset($_SESSION[$this->_prefix.'_flash'][$name])) {
+ $result = $_SESSION[$this->_prefix.'_flash'][$name];
+ unset($_SESSION[$this->_prefix.'_flash'][$name]);
+ } else {
+ $result = $default;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Checks if has flash data
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ public function hasFlash($name)
+ {
+ return isset($_SESSION[$this->_prefix.'_flash'][$name]) ? true : false;
+ }
+
+ /**
+ * Sets session name
+ *
+ * @param string $value
+ */
+ public function setSessionName($value)
+ {
+ if (empty($value)) {
+ $value = $this->_defaultSessionName;
+ }
+ session_name($value);
+ }
+
+ /**
+ * Sets session name
+ *
+ * @param string $value
+ */
+ public function setSessionPrefix($value)
+ {
+ if (empty($value)) {
+ $value = $this->_defaultSessionPrefix;
+ }
+ $this->_prefix = $value;
+ }
+
+ /**
+ * Gets session name
+ *
+ * @return string
+ */
+ public function getSessionName()
+ {
+ return session_name();
+ }
+
+ /**
+ * Destroys the session
+ */
+ public function endSession()
+ {
+ if (session_id() !== '') {
+ @session_unset();
+ @session_destroy();
+ }
+ }
+
+ /**
+ * Gets cookie mode
+ *
+ * @return string
+ */
+ public function getCookieMode()
+ {
+ if (ini_get('session.use_cookies') === '0') {
+ return 'none';
+ } elseif (ini_get('session.use_only_cookies') === '0') {
+ return 'allow';
+ } else {
+ return 'only';
+ }
+ }
+
+ /**
+ * Session open handler
+ * Do not call this method directly
+ *
+ * @param string $savePath
+ * @param string $sessionName
+ *
+ * @return boolean
+ */
+ public function openSession($savePath, $sessionName)
+ {
+ $this->_deleteExpiredSessions();
+
+ return true;
+ }
+
+ /**
+ * Session close handler
+ * Do not call this method directly
+ *
+ * @return boolean
+ */
+ public function closeSession()
+ {
+ if (session_id() !== '') {
+ @session_write_close();
+ }
+
+ return true;
+ }
+
+ /**
+ * Session read handler
+ * Do not call this method directly
+ *
+ * @param string $id
+ *
+ * @return bool
+ */
+ public function readSession($id)
+ {
+ $result = $this->_db->select(
+ 'SELECT session_data FROM `'.CConfig::get('db.prefix').'sessions` WHERE session_id = :session_id',
+ [':session_id' => $id]
+ );
+
+ // Read session data and store it into $_SESSION array
+ if (isset($result[0]['session_data'])) {
+ $dataPairs = explode('|', $result[0]['session_data']);
+
+ // Prepare array of session variables in the following format:
+ // [var_name_1] => serialized data
+ // [var_name_2] => serialized data
+ // etc.
+ $previousData = '';
+ $previousName = '';
+ $dataPairsNew = [];
+ foreach ($dataPairs as $key => $val) {
+ if ( ! empty($previousData)) {
+ $previousDataRev = strrev($previousData);
+ $po1 = strpos($previousDataRev, ';');
+ $po2 = strpos($previousDataRev, '}');
+
+ if (( ! empty($po1) && empty($po2)) || ( ! empty($po1) && ! empty($po2) && $po1 < $po2)) {
+ $divider = ';';
+ } else {
+ $divider = '}';
+ }
+
+ $previousDataParts = explode($divider, $previousData);
+ $previousDataCount = count($previousDataParts);
+ $paramName = isset($previousDataParts[$previousDataCount - 1])
+ ? $previousDataParts[$previousDataCount - 1] : '';
+ unset($previousDataParts[$previousDataCount - 1]);
+ $paramValue = implode($divider, $previousDataParts);
+
+ $dataPairsNew[$previousName] = $paramValue;
+ if ($paramValue[0] == 'a') {
+ $dataPairsNew[$previousName] .= '}';
+ }
+ } else {
+ $paramName = $val;
+ }
+ $previousName = $paramName;
+ $previousData = $val;
+ }
+
+ $dataPairsNew[$previousName] = $dataPairs[count($dataPairs) - 1];
+
+ // Store session variables in global array $_SESSION
+ foreach ($dataPairsNew as $key => $val) {
+ if ( ! empty($key)) {
+ $_SESSION[$key] = unserialize($val);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Session write handler
+ * Do not call this method directly
+ *
+ * @param string $id
+ * @param string $data
+ *
+ * @return boolean
+ */
+ public function writeSession($id, $data)
+ {
+ $result = $this->_db->select(
+ 'SELECT * from `'.CConfig::get('db.prefix').'sessions` WHERE session_id = :session_id',
+ [':session_id' => $id]
+ );
+ if (isset($result[0])) {
+ $result = $this->_db->update(
+ 'sessions',
+ [
+ 'expires_at' => time() + $this->getTimeout(),
+ 'session_data' => $data,
+ ],
+ 'session_id = :session_id',
+ [':session_id' => $id]
+ );
+ } else {
+ $result = $this->_db->insert(
+ 'sessions',
+ [
+ 'session_id' => $id,
+ 'expires_at' => time() + $this->getTimeout(),
+ 'session_data' => $data,
+ ]
+ );
+ }
+
+ return true;
+ }
+
+ /**
+ * Session destroy handler
+ * Do not call this method directly
+ *
+ * @param string $id
+ *
+ * @return boolean
+ */
+ public function destroySession($id)
+ {
+ return $this->_db->delete('sessions', "session_id = :session_id", [':session_id' => $id]);
+ }
+
+ /**
+ * Session garbage collection handler
+ * Do not call this method directly
+ *
+ * @param int $maxLifetime
+ *
+ * @return boolean
+ */
+ public function gcSession($maxLifetime)
+ {
+ return $this->_deleteExpiredSessions();
+ }
+
+ /**
+ * Sets the number of seconds after which data will be seen as 'garbage' and cleaned up
+ *
+ * @param int $value
+ */
+ public function setTimeout($value)
+ {
+ ini_set('session.gc_maxlifetime', (int)$value);
+ }
+
+ /**
+ * Returns the number of seconds after which data will be seen as 'garbage' and cleaned up
+ *
+ * @return integer
+ */
+ public function getTimeout()
+ {
+ // Get lifetime value from configuration file (in minutes)
+ $maxlifetime = CConfig::get('session.lifetime');
+
+ return ( ! empty($maxlifetime)) ? (int)($maxlifetime * 60) : (int)ini_get('session.gc_maxlifetime');
+ }
+
+ /**
+ * Starts the session if it has not started yet
+ */
+ private function _startSession()
+ {
+ // Set lifetime value from configuration file (in minutes)
+ $maxLifetime = CConfig::get('session.lifetime');
+ if ( ! empty($maxLifetime) && $maxLifetime != ini_get('session.gc_maxlifetime')) {
+ $this->setTimeout($maxLifetime);
+ }
+
+ @session_start();
+ if (APPHP_MODE == 'debug' && session_id() == '') {
+ CDebug::addMessage('errors', 'session', A::t('core', 'Failed to start session'));
+ }
+ }
+
+ /**
+ * Deletes expired sessions
+ *
+ * @return bool
+ */
+ private function _deleteExpiredSessions()
+ {
+ return $this->_db->delete('sessions', 'expires_at < :expires_at', [':expires_at' => time()]);
+ }
+
}
diff --git a/framework/components/CHttpCookie.php b/framework/components/CHttpCookie.php
index 1f4370f..60eb8d7 100644
--- a/framework/components/CHttpCookie.php
+++ b/framework/components/CHttpCookie.php
@@ -26,138 +26,159 @@
class CHttpCookie extends CComponent
{
- /** @var integer - timestamp at which the cookie expires. Default 0 means "until the browser is closed" */
- public $expire = 0;
- /** @var boolean */
- public $secure = false;
- /** @var boolean - defines whether the cookie should be accessible only through the HTTP protocol or not */
- public $httpOnly = true;
-
- /** @var string - the domain that the cookie is available to */
- private $_domain = '';
- /** @var string - path on the server where the cookie will be available on. The default is '/' */
- private $_path = '/';
-
- /**
- * Class default constructor
- */
- function __construct()
- {
- if (CConfig::get('cookies.domain') != '') $this->setDomain(CConfig::get('cookies.domain'));
- if (CConfig::get('cookies.path') != '') $this->setPath(CConfig::get('cookies.path'));
- }
-
- /**
- * Returns the instance of object
- * @return current class
- */
- public static function init()
- {
- return parent::init(__CLASS__);
- }
-
- /**
- * Sets cookie domain
- * @param string $domain
- */
- public function setDomain($domain = '')
- {
- $this->_domain = $domain;
- }
-
- /**
- * Sets cookie path
- * @param string $path
- */
- public function setPath($path = '')
- {
- $this->_path = $path;
- }
-
- /**
- * Sets cookie
- * @param string $name
- * @param mixed $value
- * @param mixed $expire
- * @param mixed $path
- * @param mixed $domain
- */
- public function set($name, $value = '', $expire = '', $path = '', $domain = '')
- {
- $expire = (!empty($expire)) ? $expire : $this->expire;
- $path = (!empty($path)) ? $path : $this->_path;
- $domain = (!empty($domain)) ? $domain : $this->_domain;
-
- setcookie($name, $value, $expire, $path, $domain, $this->secure, $this->httpOnly);
- }
-
- /**
- * Returns cookie value
- * @param string $name
- * @return mixed
- */
- public function get($name)
- {
- return isset($_COOKIE[$name]) ? $_COOKIE[$name] : '';
- }
-
- /**
- * Checks if cookie variable exists
- * @param string $name
- * @return bool
- */
- public function isExists($name)
- {
- return isset($_COOKIE[$name]) ? true : false;
- }
-
- /**
- * Deletes cookie
- * @param string $name
- * @return void
- */
- public function remove($name)
- {
- if (isset($_COOKIE[$name]) ){
- unset($_COOKIE[$name]);
- };
- setcookie($name, '', time() - 3600, $this->_path, $this->_domain, $this->secure, $this->httpOnly);
- }
-
- /**
- * Delete cookie
- * @param string $name
- * @return void|string
- */
- public function clear($name)
- {
- if (!isset($_COOKIE)) return '';
-
- if (isset($_COOKIE[$name])) {
- $this->remove($name);
- }
- }
-
- /**
- * Deletes all cookie
- * @return void|string
- */
- public function clearAll()
- {
- if (!isset($_COOKIE)) return '';
-
- foreach ($_COOKIE as $key => $value) {
- $this->remove($key);
- }
- }
-
- /**
- * Get all cookies
- */
- public function getAll()
- {
- return isset($_COOKIE) ? $_COOKIE : array();
- }
-
-
+ /** @var integer - timestamp at which the cookie expires. Default 0 means "until the browser is closed" */
+ public $expire = 0;
+ /** @var boolean */
+ public $secure = false;
+ /** @var boolean - defines whether the cookie should be accessible only through the HTTP protocol or not */
+ public $httpOnly = true;
+
+ /** @var string - the domain that the cookie is available to */
+ private $_domain = '';
+ /** @var string - path on the server where the cookie will be available on. The default is '/' */
+ private $_path = '/';
+
+ /**
+ * Class default constructor
+ */
+ function __construct()
+ {
+ if (CConfig::get('cookies.domain') != '') {
+ $this->setDomain(CConfig::get('cookies.domain'));
+ }
+ if (CConfig::get('cookies.path') != '') {
+ $this->setPath(CConfig::get('cookies.path'));
+ }
+ }
+
+ /**
+ * Returns the instance of object
+ *
+ * @return current class
+ */
+ public static function init()
+ {
+ return parent::init(__CLASS__);
+ }
+
+ /**
+ * Sets cookie domain
+ *
+ * @param string $domain
+ */
+ public function setDomain($domain = '')
+ {
+ $this->_domain = $domain;
+ }
+
+ /**
+ * Sets cookie path
+ *
+ * @param string $path
+ */
+ public function setPath($path = '')
+ {
+ $this->_path = $path;
+ }
+
+ /**
+ * Sets cookie
+ *
+ * @param string $name
+ * @param mixed $value
+ * @param mixed $expire
+ * @param mixed $path
+ * @param mixed $domain
+ */
+ public function set($name, $value = '', $expire = '', $path = '', $domain = '')
+ {
+ $expire = ( ! empty($expire)) ? $expire : $this->expire;
+ $path = ( ! empty($path)) ? $path : $this->_path;
+ $domain = ( ! empty($domain)) ? $domain : $this->_domain;
+
+ setcookie($name, $value, $expire, $path, $domain, $this->secure, $this->httpOnly);
+ }
+
+ /**
+ * Returns cookie value
+ *
+ * @param string $name
+ *
+ * @return mixed
+ */
+ public function get($name)
+ {
+ return isset($_COOKIE[$name]) ? $_COOKIE[$name] : '';
+ }
+
+ /**
+ * Checks if cookie variable exists
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ public function isExists($name)
+ {
+ return isset($_COOKIE[$name]) ? true : false;
+ }
+
+ /**
+ * Deletes cookie
+ *
+ * @param string $name
+ *
+ * @return void
+ */
+ public function remove($name)
+ {
+ if (isset($_COOKIE[$name])) {
+ unset($_COOKIE[$name]);
+ };
+ setcookie($name, '', time() - 3600, $this->_path, $this->_domain, $this->secure, $this->httpOnly);
+ }
+
+ /**
+ * Delete cookie
+ *
+ * @param string $name
+ *
+ * @return void|string
+ */
+ public function clear($name)
+ {
+ if ( ! isset($_COOKIE)) {
+ return '';
+ }
+
+ if (isset($_COOKIE[$name])) {
+ $this->remove($name);
+ }
+ }
+
+ /**
+ * Deletes all cookie
+ *
+ * @return void|string
+ */
+ public function clearAll()
+ {
+ if ( ! isset($_COOKIE)) {
+ return '';
+ }
+
+ foreach ($_COOKIE as $key => $value) {
+ $this->remove($key);
+ }
+ }
+
+ /**
+ * Get all cookies
+ */
+ public function getAll()
+ {
+ return isset($_COOKIE) ? $_COOKIE : [];
+ }
+
+
}
\ No newline at end of file
diff --git a/framework/components/CHttpRequest.php b/framework/components/CHttpRequest.php
index 4224a7b..fa09c27 100644
--- a/framework/components/CHttpRequest.php
+++ b/framework/components/CHttpRequest.php
@@ -60,951 +60,1066 @@
class CHttpRequest extends CComponent
{
-
- /** @var string */
- private $_baseUrl;
- /** @var string */
- private $_hostInfo;
- /** @var string */
- private $_hostName;
- /** @var string */
- private $_basePath = '/';
- /** @var boolean to enable cookies validation to be sure they are not tampered (defaults - false) */
- public $cookieValidation = false;
- /** @var boolean whether to enable CSRF (Cross-Site Request Forgery) validation (defaults - false) */
- private $_csrfValidation = false;
- /** @var string excluding controllers */
- private $_csrfExclude = array();
- /** @var boolean to enable gzip output compression */
- private $_gzipCompression = false;
- /** @var string */
- private $_csrfTokenKey = 'APPHP_CSRF_TOKEN';
- /** @var string */
- private $_csrfTokenValue = null;
- /** @var string session, cookie, multipages or multiforms */
- private $_csrfTokenType = 'session';
- /** @var int */
- private $_csrfMaxTokenedPages = 20;
- /** @var int port number */
- private $_port = null;
- /** @var int secure port number */
- private $_securePort = null;
- /** @var boolean
- * whether to enable referrer storage in session - has potential risk
- * requires special handling to prevent endless loops on redirection on the same page
- */
- private $_referrerInSession = false;
-
-
- /**
- * Class default constructor
- */
- function __construct()
- {
- $this->_csrfValidation = (CConfig::get('validation.csrf.enable') === true) ? true : false;
- $this->_csrfExclude = CConfig::exists('validation.csrf.exclude') ? CConfig::get('validation.csrf.exclude') : array();
- $this->_gzipCompression = (CConfig::get('compression.gzip.enable') === true) ? true : false;
- $csrfTokenType = CConfig::get('validation.csrf.tokenType');
- $this->_csrfTokenType = ($csrfTokenType !== '' && in_array($csrfTokenType, array('session', 'cookie', 'multipages', 'multiforms'))) ? $csrfTokenType : 'session';
-
- $this->_cleanRequest();
- $this->_baseUrl = $this->setBaseUrl();
- }
-
- /**
- * Triggered when invoking inaccessible methods in an object context
- * @param string $method
- * @param array $args
- * @return mixed
- */
- public function __call($method, $args)
- {
- switch (strtolower($method)) {
-
- case 'get':
- case 'post':
- case 'request':
-
- if ($method == 'get') {
- $innerGet = 'getQuery';
- $innerSet = 'setQuery';
- } elseif ($method == 'post') {
- $innerGet = 'getPost';
- $innerSet = 'setPost';
- } else {
- $innerGet = 'getRequest';
- $innerSet = 'getRequest';
- }
-
- if (count($args) == 0) {
- return $this->_getAll($method);
- } elseif (count($args) == 1) {
- return $this->$innerGet($args[0]);
- } elseif (count($args) == 2) {
- return $this->$innerSet($args[0], $args[1]);
- } elseif (count($args) == 3) {
- return $this->$innerGet($args[0], $args[1], $args[2]);
- } elseif (count($args) == 4) {
- return $this->$innerGet($args[0], $args[1], $args[2], $args[3]);
- }
-
- break;
-
- case 'getAll':
- return $this->_getAll('get');
-
- case 'postAll':
- return $this->_getAll('post');
-
- case 'postWith':
- if (count($args) == 1) {
- return $this->getPostWith($args[0]);
- }
- break;
- }
- }
-
- /**
- * Returns the instance of object
- * @return current class
- */
- public static function init()
- {
- return parent::init(__CLASS__);
- }
-
- /**
- * Strips slashes from data
- * @param mixed $data input data to be processed
- * @return mixed processed data
- */
- public function stripSlashes(&$data)
- {
- return is_array($data) ? array_map(array($this, 'stripSlashes'), $data) : stripslashes($data);
- }
-
- /**
- * Gets base URL
- * @return string
- */
- public function getBaseUrl()
- {
- return $this->_baseUrl;
- }
-
- /**
- * Gets Request URI
- * @return string
- */
- public function getRequestUri()
- {
- return isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
- }
-
- /**
- * Gets Server Name
- * @return string
- */
- public function getServerName()
- {
- return isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
- }
-
- /**
- * Gets IP address of visitor
- * @return string
- */
- public function getUserHostAddress()
- {
- return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1';
- }
-
- /**
- * Returns the schema and host part of the current request URL
- * The returned URL does not have an ending slash
- * By default this is determined based on the user request information.
- * You may explicitly specify it by setting the setHostInfo()[hostInfo]] property.
- * @return string|null
- */
- public function getHostInfo()
- {
- if ($this->_hostInfo === null) {
- $secure = $this->isSecureConnection();
- $http = $secure ? 'https' : 'http';
- if (isset($_SERVER['HTTP_HOST'])) {
- $this->_hostInfo = $http . '://' . $_SERVER['HTTP_HOST'];
- } elseif (isset($_SERVER['SERVER_NAME'])) {
- $this->_hostInfo = $http . '://' . $_SERVER['SERVER_NAME'];
- $port = $secure ? $this->getSecurePort() : $this->getPort();
- if (($port !== 80 && !$secure) || ($port !== 443 && $secure)) {
- $this->_hostInfo .= ':' . $port;
- }
- }
- }
-
- return $this->_hostInfo;
- }
-
- /**
- * Returns the host part of the current request URL (ex.: apphp.com)
- * Value is calculated from current getHostInfo()[hostInfo] property
- * @return string|null
- * @see getHostInfo()
- * @since 0.9.0
- */
- public function getHostName()
- {
- if ($this->_hostName === null) {
- $this->_hostName = parse_url($this->getHostInfo(), PHP_URL_HOST);
- }
-
- return $this->_hostName;
- }
-
- /**
- * Gets a string denoting the user agent being which is accessing the page.
- * A typical example is: Mozilla/4.5 [en] (X11; U; Linux 2.2.9 i586).
- * @return string
- */
- public function getUserAgent()
- {
- return isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
- }
-
- /**
- * Gets base path
- * @return string
- */
- public function getBasePath()
- {
- return $this->_basePath;
- }
-
- /**
- * Sets base URL
- * @param boolean $useAbsolutePath
- * @return string
- */
- public function setBaseUrl($useAbsolutePath = true)
- {
- $absolutePart = ($useAbsolutePath) ? $this->_getProtocolAndHost() : '';
-
- $scriptName = basename($_SERVER['SCRIPT_FILENAME']);
- if (basename($_SERVER['SCRIPT_NAME']) === $scriptName) {
- $scriptUrl = $_SERVER['SCRIPT_NAME'];
- } elseif (basename($_SERVER['PHP_SELF']) === $scriptName) {
- $scriptUrl = $_SERVER['PHP_SELF'];
- } elseif (isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME']) === $scriptName) {
- $scriptUrl = $_SERVER['ORIG_SCRIPT_NAME'];
- } elseif (($pos = strpos($_SERVER['PHP_SELF'], '/' . $scriptName)) !== false) {
- $scriptUrl = substr($_SERVER['SCRIPT_NAME'], 0, $pos) . '/' . $scriptName;
- } elseif (isset($_SERVER['DOCUMENT_ROOT']) && strpos($_SERVER['SCRIPT_FILENAME'], $_SERVER['DOCUMENT_ROOT']) === 0) {
- $scriptUrl = str_replace('\\', '/', str_replace($_SERVER['DOCUMENT_ROOT'], '', $_SERVER['SCRIPT_FILENAME']));
- } else {
- CDebug::addMessage('error', 'entry_script', A::t('core', 'Framework is unable to determine the entry script URL'));
- }
-
- $this->_basePath = rtrim(dirname($scriptUrl), '\\/') . '/';
-
- return $absolutePart . $this->_basePath;
- }
-
- /**
- * Returns parameter from global array $_GET
- * @param string $name
- * @param string|array $filters
- * @param string $default
- * @param array $allowedValues
- * @see CFilter
- * @return mixed
- */
- public function getQuery($name = '', $filters = '', $default = '', $allowedValues = array())
- {
- if (empty($name)) {
- return $this->_getAll('get');
- } else {
- return $this->_getParam('get', $name, $default, $filters, $allowedValues);
- }
- }
-
- /**
- * Sets value to global array $_GET
- * @param string $name
- * @param string $value
- * @return bool
- */
- public function setQuery($name, $value = '')
- {
- if (isset($_GET)) {
- $_GET[$name] = $value;
- return true;
- }
-
- return false;
- }
-
- /**
- * Returns parameter from global array $_POST
- * @param string $name
- * @param string|array $filters
- * @param string $default
- * @param array $allowedValues
- * @see CFilter
- * @return mixed
- */
- public function getPost($name = '', $filters = '', $default = '', $allowedValues = array())
- {
- if (empty($name)) {
- return $this->_getAll('post');
- } else {
- return $this->_getParam('post', $name, $default, $filters, $allowedValues);
- }
- }
-
- /**
- * Returns variables from global array $_POST with certain parameter
- * @param string $name
- * @param string|array $filters
- * @param string $default
- * @param array $allowedValues
- * @return array
- */
- public function getPostWith($name, $filters = '', $default = '', $allowedValues = array())
- {
- $result = array();
- if (!isset($_POST) || !is_array($_POST)) return $result;
-
- foreach ($_POST as $key => $val) {
- if (preg_match('/' . $name . '/i', $key)) {
- $result[$key] = $this->_getParam('post', $key, $default, $filters, $allowedValues);
- }
- }
-
- return $result;
- }
-
- /**
- * Sets value to global array $_POST
- * @param string $name
- * @param string $value
- * @return bool
- */
- public function setPost($name, $value = '')
- {
- if (isset($_POST)) {
- $_POST[$name] = $value;
- return true;
- }
-
- return false;
- }
-
- /**
- * Returns parameter from global array $_GET or $_POST
- * @param string $name
- * @param string|array $filters
- * @param string $default
- * @param array $allowedValues
- * @return mixed
- */
- public function getRequest($name = '', $filters = '', $default = '', $allowedValues = array())
- {
- if (empty($name)) {
- return $this->_getAll('request');
- } else {
- return $this->_getParam('request', $name, $default, $filters, $allowedValues);
- }
- }
-
- /**
- * Returns whether there is an AJAX (XMLHttpRequest) request
- * @return boolean
- * @since 0.6.0
- */
- public function isAjaxRequest()
- {
- return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest';
- }
-
- /**
- * Returns whether there is a PUT request
- * @return boolean
- * @since 0.6.0
- */
- public function isPutRequest()
- {
- return isset($_SERVER['REQUEST_METHOD']) && !strcasecmp($_SERVER['REQUEST_METHOD'], 'PUT');
- }
-
- /**
- * Returns whether there is a DELETE request
- * @return boolean
- * @since 0.6.0
- */
- public function isDeleteRequest()
- {
- return isset($_SERVER['REQUEST_METHOD']) && !strcasecmp($_SERVER['REQUEST_METHOD'], 'DELETE');
- }
-
- /**
- * Returns whether there is a POST request
- * @return boolean
- */
- public function isPostRequest()
- {
- return isset($_SERVER['REQUEST_METHOD']) && !strcasecmp($_SERVER['REQUEST_METHOD'], 'POST');
- }
-
- /**
- * Returns whether there is a POST variable exists
- * @param string $name
- * @return boolean
- */
- public function isPostExists($name)
- {
- return isset($_POST[$name]);
- }
-
- /**
- * Return if the request is sent via secure channel (https)
- * @return boolean
- */
- public function isSecureConnection()
- {
- return (isset($_SERVER['HTTPS']) && (strcasecmp($_SERVER['HTTPS'], 'on') === 0 || $_SERVER['HTTPS'] == 1))
- ||
- (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0);
- }
-
- /**
- * Returns if csrf validation is used
- * @return string
- */
- public function getCsrfValidation()
- {
- if (!empty($this->_csrfExclude) && is_array($this->_csrfExclude)) {
- // Retrirve current controller
- // TODO: this is a simplest code, we need to improve it and use URL rules
- $request = isset($_GET['url']) ? $_GET['url'] : '';
- $split = explode('/', trim($request, '/'));
- $controller = !empty($split[0]) ? $split[0] : CConfig::get('defaultController');
-
- if (in_array(strtolower($controller), array_map('strtolower', $this->_csrfExclude))) {
- return false;
- }
- }
-
- return $this->_csrfValidation;
- }
-
- /**
- * Returns CSRF token key name
- * @return string
- */
- public function getCsrfTokenKey()
- {
- return $this->_csrfTokenKey;
- }
-
- /**
- * Returns the random token value used to perform CSRF validation
- * @param string $formId
- * @return string
- * @see $this->_csrfValidation()
- */
- public function getCsrfTokenValue($formId = '')
- {
- // Check and set token
- $csrfTokenValue = md5(uniqid(rand(), true));
-
- if ($this->_csrfTokenType == 'session') {
- if ($this->_csrfTokenValue === null) {
- $this->_csrfTokenValue = $csrfTokenValue;
- A::app()->getSession()->set('token', $this->_csrfTokenValue);
- }
- } elseif ($this->_csrfTokenType == 'cookie') {
- if ($this->_csrfTokenValue === null) {
- $this->_csrfTokenValue = $csrfTokenValue;
- A::app()->getCookie()->set('token', $this->_csrfTokenValue);
- }
- } elseif ($this->_csrfTokenType == 'multipages' || $this->_csrfTokenType == 'multiforms') {
- if ($this->_csrfTokenType == 'multipages') {
- // Get page ID
- $tokenId = $this->_getControllerAndAction();
- } else {
- $tokenId = $formId;
- }
- if (CString::length($tokenId) < 100 && CValidator::isVariable($tokenId)) {
- if ($this->_csrfTokenValue === null) {
- $this->_csrfTokenValue = $csrfTokenValue;
- }
- // Get array and validate
- $csrfTokenValues = A::app()->getSession()->get('token');
- if (empty($csrfTokenValues) || !is_array($csrfTokenValues)) {
- $csrfTokenValues = array();
- }
- // Save data
- $csrfTokenValues[$tokenId] = ($this->_csrfTokenValue !== null) ? $this->_csrfTokenValue : $csrfTokenValue;
- if (count($csrfTokenValues) > $this->_csrfMaxTokenedPages) {
- array_shift($csrfTokenValues);
- }
- A::app()->getSession()->set('token', $csrfTokenValues);
- }
- }
-
- return $this->_csrfTokenValue;
- }
-
- /**
- * Performs the CSRF validation
- * @param string $formId
- * @return void
- */
- public function validateCsrfToken($formId = '')
- {
- // Validate only POST requests
- if ($this->isPostRequest()) {
-
- $valid = false;
- $tokenFromPost = isset($_POST[$this->_csrfTokenKey]) ? $_POST[$this->_csrfTokenKey] : null;
-
- if ($this->_csrfTokenType == 'session') {
- if (A::app()->getSession()->isExists('token') && isset($_POST[$this->_csrfTokenKey])) {
- $tokenFromSession = A::app()->getSession()->get('token');
- $valid = ($tokenFromSession === $tokenFromPost);
- }
- } elseif ($this->_csrfTokenType == 'cookie') {
- if (A::app()->getCookie()->isExists('token') && isset($_POST[$this->_csrfTokenKey])) {
- $tokenFromCookie = A::app()->getCookie()->get('token');
- $valid = ($tokenFromCookie === $tokenFromPost);
- }
- } elseif ($this->_csrfTokenType == 'multipages' || $this->_csrfTokenType == 'multiforms') {
- if ($this->_csrfTokenType == 'multipages') {
- // Get page ID
- $tokenId = $this->_getControllerAndAction();
- } else {
- $tokenId = $formId;
- }
- if (A::app()->getSession()->isExists('token') && isset($_POST[$this->_csrfTokenKey])) {
- $tokenFromSession = A::app()->getSession()->get('token');
- $tokenFromSession = isset($tokenFromSession[$tokenId]) ? $tokenFromSession[$tokenId] : '';
- $valid = ($tokenFromSession === $tokenFromPost);
- }
- }
-
- if (!$valid) {
- unset($_POST);
- A::app()->getSession()->setFlash(
- 'csrfError',
- CWidget::create('CMessage', array('warning', A::t('core', 'The CSRF token has expired or invalid. Please try to
refresh page and resubmit the form.')))
- );
- CDebug::addMessage('warnings', 'csrf_token', A::t('core', 'The CSRF token could not be verified.'), 'session');
- A::app()->getClientScript()->registerScript(
- 'csrfError',
- 'function csrf_refresh_page(){location.href = location.href + \'?\' + Math.random();}',
- 2
- );
- }
- }
- }
-
- /**
- * Set GZIP compression handler
- */
- public function setGzipHandler()
- {
- if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')) {
- // Fix for warning: ob_start() [ref.outcontrol]: output handler 'ob_gzhandler' conflicts with zlib output compression'
- if (extension_loaded('zlib')) {
- if (ob_get_length()) {
- ob_end_clean();
- }
- }
- ob_start('ob_gzhandler');
- } else {
- ob_start();
- }
- }
-
- /**
- * Cleans the request data
- * This method removes slashes from request data if get_magic_quotes_gpc() is turned on
- * Also performs CSRF validation if {@link _csrfValidation} is true
- */
- protected function _cleanRequest()
- {
- // 01.12.2018 - DEPRECATED
- // Clean request only for PHP < 5.3, in greater versions of PHP 'magic' functions are deprecated
- //if(version_compare(phpversion(), '5.3.0', '<')){
- // if(function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()){
- // $_GET = $this->stripSlashes($_GET);
- // $_POST = $this->stripSlashes($_POST);
- // $_REQUEST = $this->stripSlashes($_REQUEST);
- // $_COOKIE = $this->stripSlashes($_COOKIE);
- // }
- //}
-
- if ($this->getCsrfValidation()) A::app()->attachEventHandler('_onBeginRequest', array($this, 'validateCsrfToken'));
- if ($this->_gzipCompression) A::app()->attachEventHandler('_onBeginRequest', array($this, 'setGzipHandler'));
- if ($this->_referrerInSession) A::app()->attachEventHandler('_onBeginRequest', array($this, 'setHttpReferrer'));
- }
-
- /**
- * Returns protocol and host
- * @param bool $usePort
- * @return string
- */
- protected function _getProtocolAndHost($usePort = true)
- {
- $protocol = 'http://';
- $port = '';
- $httpHost = isset($_SERVER['HTTP_HOST']) ? htmlentities($_SERVER['HTTP_HOST']) : '';
- $serverProtocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : '';
-
- if ((isset($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) != 'off')) || strtolower(substr($serverProtocol, 0, 5)) == 'https') {
- $protocol = 'https://';
- }
-
- if ($usePort) {
- $portNumber = $this->getPort();
- if ($portNumber != '80' && !strpos($httpHost, ':')) {
- $port = ':' . $portNumber;
- }
- }
-
- return $protocol . $httpHost . $port;
- }
-
- /**
- * Returns controller and action from URL
- * @return string
- */
- protected function _getControllerAndAction()
- {
- $request = isset($_GET['url']) ? $_GET['url'] : '';
- $split = explode('/', trim($request, '/'));
- $pageId = isset($split[0]) ? $split[0] : '';
- $pageId .= isset($split[1]) ? '_' . $split[1] : '';
-
- return $pageId;
- }
-
- /**
- * Returns parameter from global arrays $_GET or $_POST according to the type of request
- * @param string $type
- * @param string $name
- * @param string|array $filters
- * @param array $allowedValues
- * @param string $default
- * @return mixed
- */
- private function _getParam($type = 'get', $name = '', $default = '', $filters = '', $allowedValues = array())
- {
- $value = null;
-
- if ($type == 'get') {
- if (isset($_GET[$name])) {
- $value = $_GET[$name];
- } else {
- // Check for variant
- // URL: http://localhost/site/page/contact/param1/aaa/param2/bbb/param3/ccc
- $request = isset($_GET['url']) ? $_GET['url'] : '';
- $split = explode('/', trim($request, '/'));
-
- $temp = array();
- foreach ($split as $index => $part) {
- if (!$temp || end($temp) !== null) {
- $temp[$part] = null;
- } else {
- $arrayArg = array_keys($temp);
- $tempEnd = end($arrayArg);
- $temp[$tempEnd] = $part;
- }
- }
- $temp = array_slice($temp, 1);
- if (isset($temp[$name])) $value = $temp[$name];
- }
- } elseif ($type == 'post' && isset($_POST[$name])) {
- $value = $_POST[$name];
- } elseif ($type == 'request' && (isset($_GET[$name]) || isset($_POST[$name]))) {
- $value = isset($_GET[$name]) ? $_GET[$name] : $_POST[$name];
- }
-
- if ($value !== null) {
- // Validate allowed values
- if (!empty($allowedValues)) {
- if (!is_array($allowedValues)) $allowedValues = array($allowedValues);
- if (!in_array($value, $allowedValues)) {
- $value = $default;
- }
- }
-
- // Filter values
- if (!empty($filters)) {
- if (!is_array($filters)) $filters = array($filters);
- foreach ($filters as $filter) {
- $value = CFilter::sanitize($filter, $value);
- }
- }
- } else {
- $value = $default;
- }
-
- return $value;
- }
-
- /**
- * Returns global arrays: $_GET, $_POST or $_REQUEST according to given type
- * @param string $type
- * @return array
- */
- private function _getAll($type = 'get')
- {
- if ($type == 'get') {
- return isset($_GET) ? $_GET : array();
- } elseif ($type == 'post') {
- return isset($_POST) ? $_POST : array();
- } elseif ($type == 'request' && (isset($_GET) || isset($_POST))) {
- return isset($_GET) ? $_GET : $_POST;
- }
-
- return array();
- }
-
- /**
- * Downloads a content as file from browser to user
- * @param string $fileName
- * @param string $content
- * @param string $mimeType
- * @param boolean $terminate
- */
- public function downloadContent($fileName, $content, $mimeType = null, $terminate = true)
- {
- if ($mimeType === null) $mimeType = 'text/plain';
-
- header('Pragma: public');
- header('Expires: 0');
- header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
- header('Content-type: ' . $mimeType);
- if (ob_get_length() === false) {
- header('Content-Length: ' . (function_exists('mb_strlen') ? mb_strlen($content, '8bit') : strlen($content)));
- }
- header('Content-Disposition: attachment; filename="' . $fileName . '"');
- header('Content-Transfer-Encoding: binary');
- echo $content;
-
- if ($terminate) exit(0);
- }
-
- /**
- * Downloads a file from browser to user
- * @param string $file
- * @param string $mimeType
- * @param boolean $terminate
- */
- public function downloadFile($file, $mimeType = null, $terminate = true)
- {
- if ($mimeType === null) {
- $mimeType = CFile::getMimeType($file);
- }
-
- header('Pragma: public');
- header('Expires: 0');
- header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
- header('Content-type: ' . $mimeType);
- header('Content-Length: ' . filesize($file));
- header('Content-Disposition: attachment; filename="' . basename($file) . '"');
- header('Content-Transfer-Encoding: binary');
- readfile($file);
-
- if ($terminate) exit(0);
- }
-
- /**
- * Returns information about the browser of user
- * @param string $key
- * @param string $userAgent
- * @return array
- * @see http://www.php.net/manual/en/function.get-browser.php
- */
- public function getBrowser($key = '', $userAgent = null)
- {
- $browser = get_browser($userAgent, true);
-
- if (!empty($key)) {
- return isset($browser[$key]) ? $browser[$key] : '';
- }
-
- return $browser;
- }
-
- /**
- * Sets HTTP Referrer
- * Has potential risk because can insert current URL as a referrer and lead to endless loops on redirection
- * Ex.: language or currency changes
- */
- public function setHttpReferrer()
- {
- // Save current data as previous referrer
- A::app()->getSession()->set('http_referrer_previous', A::app()->getSession()->get('http_referrer_current'));
- // Save current link as referrer
- $httpReferrerCurrent = $this->_getProtocolAndHost() . $this->getRequestUri();
- A::app()->getSession()->set('http_referrer_current', $httpReferrerCurrent);
- }
-
- /**
- * Returns the URL referrer, null if not present
- */
- public function getUrlReferrer()
- {
- $serverReferrer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : null;
- $sessionReferrer = A::app()->getSession()->get('http_referrer_previous');
-
- if (!empty($serverReferrer)) {
- return $serverReferrer;
- } elseif (!empty($sessionReferrer)) {
- return $sessionReferrer;
- } else {
- return null;
- }
- }
-
- /**
- * Returns the port to use for insecure requests
- * Defaults to 80 or the port specified by the server (if the current request is insecure)
- * @return int
- * @since 0.7.0
- */
- public function getPort()
- {
- if ($this->_port === null) {
- $this->_port = !$this->isSecureConnection() && isset($_SERVER['SERVER_PORT']) ? (int)$_SERVER['SERVER_PORT'] : 80;
- }
-
- return $this->_port;
- }
-
- /**
- * Returns the port to use for secure requests
- * Defaults to 443, or the port specified by the server (if the current request is secure)
- * @return int
- * @since 0.7.0
- */
- public function getSecurePort()
- {
- if ($this->_securePort === null) {
- $this->_securePort = $this->isSecureConnection() && isset($_SERVER['SERVER_PORT']) ? (int)$_SERVER['SERVER_PORT'] : 443;
- }
-
- return $this->_securePort;
- }
-
- /**
- * Returns content of the given URL
- * @param string $url
- * @param string $method
- * @param string $data
- * @param string $params
- * @param string $function 'file_get_contents' or 'curl'
- * @return mixed
- */
- function getUrlContent($url = '', $method = 'get', $data = array(), $params = array(), $function = 'file_get_contents')
- {
-
- # Validate function argumanets
- $method = strtolower($method);
- $data = (array)$data;
-
- if (empty($url) && !in_array($method, array('get', 'post'))) {
- return true;
- }
-
- # Get parameters
- $ajaxCall = isset($params['ajax']) ? (bool)$params['ajax'] : false;
- $showErrors = isset($params['errors']) ? (bool)$params['errors'] : false;
- $json = isset($params['json']) ? (bool)$params['json'] : false;
- $sslVerifyHost = isset($params['ssl_verify_host']) ? (bool)$params['ssl_verify_host'] : false;
- $sslVerifyPeer = isset($params['ssl_verify_peer']) ? (bool)$params['ssl_verify_peer'] : false;
- $result = NULL;
-
- if ($function == 'curl') {
- # Init curl
- $ch = curl_init();
-
- # Set options
- curl_setopt($ch, CURLOPT_URL, $url);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0);
- curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322)');
-
- # Fake AJAX call
- if ($ajaxCall) {
- curl_setopt($ch, CURLOPT_HTTPHEADER, array("X-Requested-With: XMLHttpRequest"));
- }
-
- # SSL verification
- curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, ($sslVerifyHost ? 2 : 0));
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, ($sslVerifyPeer ? 1 : 0));
-
- if ($method == 'post') {
- # Set the HEADER, number of POST vars, POST data
- if ($json) {
- curl_setopt($ch, CURLOPT_HEADER, false);
- curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-type: application/json"));
- curl_setopt($ch, CURLOPT_POST, count($data));
- curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
- } else {
- curl_setopt($ch, CURLOPT_POST, count($data));
- curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
- curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
- }
- }
-
- if ($showErrors) {
- # Check for errors and include in the error message
- $error = '';
- if ($errno = curl_errno($ch)) {
- $errorMessage = function_exists('curl_strerror') ? curl_strerror($errno) : '';
- $error = "cURL error ({$errno}):\n {$errorMessage}";
- }
-
- $result['result'] = curl_exec($ch);
- $result['error'] = $error;
- } else {
- $result = curl_exec($ch);
- }
-
- # Close connection
- curl_close($ch);
- } else {
- $context = NULL;
-
- # Use key 'http' even if you send the request to https://
- if ($method == 'post') {
- $options = array(
- 'http' => array(
- 'header' => "Content-type: application/x-www-form-urlencoded\r\n" .
- ($ajaxCall ? "X-Requested-With: XMLHttpRequest\r\n" : ''),
- 'method' => 'POST',
- 'content' => http_build_query($data),
- ),
- );
-
- # Disable SSL verification
- if (!$sslVerifyPeer) {
- $options['ssl'] = array(
- 'verify_peer' => false,
- 'verify_peer_name' => false,
- );
- }
-
- $context = stream_context_create($options);
- }
-
- $result = file_get_contents($url, false, $context);
- }
-
- return $result;
- }
-
+
+ /** @var string */
+ private $_baseUrl;
+ /** @var string */
+ private $_hostInfo;
+ /** @var string */
+ private $_hostName;
+ /** @var string */
+ private $_basePath = '/';
+ /** @var boolean to enable cookies validation to be sure they are not tampered (defaults - false) */
+ public $cookieValidation = false;
+ /** @var boolean whether to enable CSRF (Cross-Site Request Forgery) validation (defaults - false) */
+ private $_csrfValidation = false;
+ /** @var string excluding controllers */
+ private $_csrfExclude = [];
+ /** @var boolean to enable gzip output compression */
+ private $_gzipCompression = false;
+ /** @var string */
+ private $_csrfTokenKey = 'APPHP_CSRF_TOKEN';
+ /** @var string */
+ private $_csrfTokenValue = null;
+ /** @var string session, cookie, multipages or multiforms */
+ private $_csrfTokenType = 'session';
+ /** @var int */
+ private $_csrfMaxTokenedPages = 20;
+ /** @var int port number */
+ private $_port = null;
+ /** @var int secure port number */
+ private $_securePort = null;
+ /** @var boolean
+ * whether to enable referrer storage in session - has potential risk
+ * requires special handling to prevent endless loops on redirection on the same page
+ */
+ private $_referrerInSession = false;
+
+
+ /**
+ * Class default constructor
+ */
+ function __construct()
+ {
+ $this->_csrfValidation = (CConfig::get('validation.csrf.enable') === true) ? true : false;
+ $this->_csrfExclude = CConfig::exists('validation.csrf.exclude') ? CConfig::get('validation.csrf.exclude')
+ : [];
+ $this->_gzipCompression = (CConfig::get('compression.gzip.enable') === true) ? true : false;
+ $csrfTokenType = CConfig::get('validation.csrf.tokenType');
+ $this->_csrfTokenType = ($csrfTokenType !== ''
+ && in_array(
+ $csrfTokenType,
+ ['session', 'cookie', 'multipages', 'multiforms']
+ )) ? $csrfTokenType : 'session';
+
+ $this->_cleanRequest();
+ $this->_baseUrl = $this->setBaseUrl();
+ }
+
+ /**
+ * Triggered when invoking inaccessible methods in an object context
+ *
+ * @param string $method
+ * @param array $args
+ *
+ * @return mixed
+ */
+ public function __call($method, $args)
+ {
+ switch (strtolower($method)) {
+ case 'get':
+ case 'post':
+ case 'request':
+
+ if ($method == 'get') {
+ $innerGet = 'getQuery';
+ $innerSet = 'setQuery';
+ } elseif ($method == 'post') {
+ $innerGet = 'getPost';
+ $innerSet = 'setPost';
+ } else {
+ $innerGet = 'getRequest';
+ $innerSet = 'getRequest';
+ }
+
+ if (count($args) == 0) {
+ return $this->_getAll($method);
+ } elseif (count($args) == 1) {
+ return $this->$innerGet($args[0]);
+ } elseif (count($args) == 2) {
+ return $this->$innerSet($args[0], $args[1]);
+ } elseif (count($args) == 3) {
+ return $this->$innerGet($args[0], $args[1], $args[2]);
+ } elseif (count($args) == 4) {
+ return $this->$innerGet($args[0], $args[1], $args[2], $args[3]);
+ }
+
+ break;
+
+ case 'getAll':
+ return $this->_getAll('get');
+
+ case 'postAll':
+ return $this->_getAll('post');
+
+ case 'postWith':
+ if (count($args) == 1) {
+ return $this->getPostWith($args[0]);
+ }
+ break;
+ }
+ }
+
+ /**
+ * Returns the instance of object
+ *
+ * @return current class
+ */
+ public static function init()
+ {
+ return parent::init(__CLASS__);
+ }
+
+ /**
+ * Strips slashes from data
+ *
+ * @param mixed $data input data to be processed
+ *
+ * @return mixed processed data
+ */
+ public function stripSlashes(&$data)
+ {
+ return is_array($data) ? array_map([$this, 'stripSlashes'], $data) : stripslashes($data);
+ }
+
+ /**
+ * Gets base URL
+ *
+ * @return string
+ */
+ public function getBaseUrl()
+ {
+ return $this->_baseUrl;
+ }
+
+ /**
+ * Gets Request URI
+ *
+ * @return string
+ */
+ public function getRequestUri()
+ {
+ return isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
+ }
+
+ /**
+ * Gets Server Name
+ *
+ * @return string
+ */
+ public function getServerName()
+ {
+ return isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
+ }
+
+ /**
+ * Gets IP address of visitor
+ *
+ * @return string
+ */
+ public function getUserHostAddress()
+ {
+ return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1';
+ }
+
+ /**
+ * Returns the schema and host part of the current request URL
+ * The returned URL does not have an ending slash
+ * By default this is determined based on the user request information.
+ * You may explicitly specify it by setting the setHostInfo()[hostInfo]] property.
+ *
+ * @return string|null
+ */
+ public function getHostInfo()
+ {
+ if ($this->_hostInfo === null) {
+ $secure = $this->isSecureConnection();
+ $http = $secure ? 'https' : 'http';
+ if (isset($_SERVER['HTTP_HOST'])) {
+ $this->_hostInfo = $http.'://'.$_SERVER['HTTP_HOST'];
+ } elseif (isset($_SERVER['SERVER_NAME'])) {
+ $this->_hostInfo = $http.'://'.$_SERVER['SERVER_NAME'];
+ $port = $secure ? $this->getSecurePort() : $this->getPort();
+ if (($port !== 80 && ! $secure) || ($port !== 443 && $secure)) {
+ $this->_hostInfo .= ':'.$port;
+ }
+ }
+ }
+
+ return $this->_hostInfo;
+ }
+
+ /**
+ * Returns the host part of the current request URL (ex.: apphp.com)
+ * Value is calculated from current getHostInfo()[hostInfo] property
+ *
+ * @return string|null
+ * @see getHostInfo()
+ * @since 0.9.0
+ */
+ public function getHostName()
+ {
+ if ($this->_hostName === null) {
+ $this->_hostName = parse_url($this->getHostInfo(), PHP_URL_HOST);
+ }
+
+ return $this->_hostName;
+ }
+
+ /**
+ * Gets a string denoting the user agent being which is accessing the page.
+ * A typical example is: Mozilla/4.5 [en] (X11; U; Linux 2.2.9 i586).
+ *
+ * @return string
+ */
+ public function getUserAgent()
+ {
+ return isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
+ }
+
+ /**
+ * Gets base path
+ *
+ * @return string
+ */
+ public function getBasePath()
+ {
+ return $this->_basePath;
+ }
+
+ /**
+ * Sets base URL
+ *
+ * @param boolean $useAbsolutePath
+ *
+ * @return string
+ */
+ public function setBaseUrl($useAbsolutePath = true)
+ {
+ $absolutePart = ($useAbsolutePath) ? $this->_getProtocolAndHost() : '';
+
+ $scriptName = basename($_SERVER['SCRIPT_FILENAME']);
+ if (basename($_SERVER['SCRIPT_NAME']) === $scriptName) {
+ $scriptUrl = $_SERVER['SCRIPT_NAME'];
+ } elseif (basename($_SERVER['PHP_SELF']) === $scriptName) {
+ $scriptUrl = $_SERVER['PHP_SELF'];
+ } elseif (isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME']) === $scriptName) {
+ $scriptUrl = $_SERVER['ORIG_SCRIPT_NAME'];
+ } elseif (($pos = strpos($_SERVER['PHP_SELF'], '/'.$scriptName)) !== false) {
+ $scriptUrl = substr($_SERVER['SCRIPT_NAME'], 0, $pos).'/'.$scriptName;
+ } elseif (isset($_SERVER['DOCUMENT_ROOT'])
+ && strpos($_SERVER['SCRIPT_FILENAME'], $_SERVER['DOCUMENT_ROOT']) === 0
+ ) {
+ $scriptUrl = str_replace(
+ '\\',
+ '/',
+ str_replace($_SERVER['DOCUMENT_ROOT'], '', $_SERVER['SCRIPT_FILENAME'])
+ );
+ } else {
+ CDebug::addMessage(
+ 'error',
+ 'entry_script',
+ A::t('core', 'Framework is unable to determine the entry script URL')
+ );
+ }
+
+ $this->_basePath = rtrim(dirname($scriptUrl), '\\/').'/';
+
+ return $absolutePart.$this->_basePath;
+ }
+
+ /**
+ * Returns parameter from global array $_GET
+ *
+ * @param string $name
+ * @param string|array $filters
+ * @param string $default
+ * @param array $allowedValues
+ *
+ * @return mixed
+ * @see CFilter
+ */
+ public function getQuery($name = '', $filters = '', $default = '', $allowedValues = [])
+ {
+ if (empty($name)) {
+ return $this->_getAll('get');
+ } else {
+ return $this->_getParam('get', $name, $default, $filters, $allowedValues);
+ }
+ }
+
+ /**
+ * Sets value to global array $_GET
+ *
+ * @param string $name
+ * @param string $value
+ *
+ * @return bool
+ */
+ public function setQuery($name, $value = '')
+ {
+ if (isset($_GET)) {
+ $_GET[$name] = $value;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns parameter from global array $_POST
+ *
+ * @param string $name
+ * @param string|array $filters
+ * @param string $default
+ * @param array $allowedValues
+ *
+ * @return mixed
+ * @see CFilter
+ */
+ public function getPost($name = '', $filters = '', $default = '', $allowedValues = [])
+ {
+ if (empty($name)) {
+ return $this->_getAll('post');
+ } else {
+ return $this->_getParam('post', $name, $default, $filters, $allowedValues);
+ }
+ }
+
+ /**
+ * Returns variables from global array $_POST with certain parameter
+ *
+ * @param string $name
+ * @param string|array $filters
+ * @param string $default
+ * @param array $allowedValues
+ *
+ * @return array
+ */
+ public function getPostWith($name, $filters = '', $default = '', $allowedValues = [])
+ {
+ $result = [];
+ if ( ! isset($_POST) || ! is_array($_POST)) {
+ return $result;
+ }
+
+ foreach ($_POST as $key => $val) {
+ if (preg_match('/'.$name.'/i', $key)) {
+ $result[$key] = $this->_getParam('post', $key, $default, $filters, $allowedValues);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Sets value to global array $_POST
+ *
+ * @param string $name
+ * @param string $value
+ *
+ * @return bool
+ */
+ public function setPost($name, $value = '')
+ {
+ if (isset($_POST)) {
+ $_POST[$name] = $value;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns parameter from global array $_GET or $_POST
+ *
+ * @param string $name
+ * @param string|array $filters
+ * @param string $default
+ * @param array $allowedValues
+ *
+ * @return mixed
+ */
+ public function getRequest($name = '', $filters = '', $default = '', $allowedValues = [])
+ {
+ if (empty($name)) {
+ return $this->_getAll('request');
+ } else {
+ return $this->_getParam('request', $name, $default, $filters, $allowedValues);
+ }
+ }
+
+ /**
+ * Returns whether there is an AJAX (XMLHttpRequest) request
+ *
+ * @return boolean
+ * @since 0.6.0
+ */
+ public function isAjaxRequest()
+ {
+ return isset($_SERVER['HTTP_X_REQUESTED_WITH'])
+ && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest';
+ }
+
+ /**
+ * Returns whether there is a PUT request
+ *
+ * @return boolean
+ * @since 0.6.0
+ */
+ public function isPutRequest()
+ {
+ return isset($_SERVER['REQUEST_METHOD']) && ! strcasecmp($_SERVER['REQUEST_METHOD'], 'PUT');
+ }
+
+ /**
+ * Returns whether there is a DELETE request
+ *
+ * @return boolean
+ * @since 0.6.0
+ */
+ public function isDeleteRequest()
+ {
+ return isset($_SERVER['REQUEST_METHOD']) && ! strcasecmp($_SERVER['REQUEST_METHOD'], 'DELETE');
+ }
+
+ /**
+ * Returns whether there is a POST request
+ *
+ * @return boolean
+ */
+ public function isPostRequest()
+ {
+ return isset($_SERVER['REQUEST_METHOD']) && ! strcasecmp($_SERVER['REQUEST_METHOD'], 'POST');
+ }
+
+ /**
+ * Returns whether there is a POST variable exists
+ *
+ * @param string $name
+ *
+ * @return boolean
+ */
+ public function isPostExists($name)
+ {
+ return isset($_POST[$name]);
+ }
+
+ /**
+ * Return if the request is sent via secure channel (https)
+ *
+ * @return boolean
+ */
+ public function isSecureConnection()
+ {
+ return (isset($_SERVER['HTTPS']) && (strcasecmp($_SERVER['HTTPS'], 'on') === 0 || $_SERVER['HTTPS'] == 1))
+ || (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
+ && strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0);
+ }
+
+ /**
+ * Returns if csrf validation is used
+ *
+ * @return string
+ */
+ public function getCsrfValidation()
+ {
+ if ( ! empty($this->_csrfExclude) && is_array($this->_csrfExclude)) {
+ // Retrirve current controller
+ // TODO: this is a simplest code, we need to improve it and use URL rules
+ $request = isset($_GET['url']) ? $_GET['url'] : '';
+ $split = explode('/', trim($request, '/'));
+ $controller = ! empty($split[0]) ? $split[0] : CConfig::get('defaultController');
+
+ if (in_array(strtolower($controller), array_map('strtolower', $this->_csrfExclude))) {
+ return false;
+ }
+ }
+
+ return $this->_csrfValidation;
+ }
+
+ /**
+ * Returns CSRF token key name
+ *
+ * @return string
+ */
+ public function getCsrfTokenKey()
+ {
+ return $this->_csrfTokenKey;
+ }
+
+ /**
+ * Returns the random token value used to perform CSRF validation
+ *
+ * @param string $formId
+ *
+ * @return string
+ * @see $this->_csrfValidation()
+ */
+ public function getCsrfTokenValue($formId = '')
+ {
+ // Check and set token
+ $csrfTokenValue = md5(uniqid(rand(), true));
+
+ if ($this->_csrfTokenType == 'session') {
+ if ($this->_csrfTokenValue === null) {
+ $this->_csrfTokenValue = $csrfTokenValue;
+ A::app()->getSession()->set('token', $this->_csrfTokenValue);
+ }
+ } elseif ($this->_csrfTokenType == 'cookie') {
+ if ($this->_csrfTokenValue === null) {
+ $this->_csrfTokenValue = $csrfTokenValue;
+ A::app()->getCookie()->set('token', $this->_csrfTokenValue);
+ }
+ } elseif ($this->_csrfTokenType == 'multipages' || $this->_csrfTokenType == 'multiforms') {
+ if ($this->_csrfTokenType == 'multipages') {
+ // Get page ID
+ $tokenId = $this->_getControllerAndAction();
+ } else {
+ $tokenId = $formId;
+ }
+ if (CString::length($tokenId) < 100 && CValidator::isVariable($tokenId)) {
+ if ($this->_csrfTokenValue === null) {
+ $this->_csrfTokenValue = $csrfTokenValue;
+ }
+ // Get array and validate
+ $csrfTokenValues = A::app()->getSession()->get('token');
+ if (empty($csrfTokenValues) || ! is_array($csrfTokenValues)) {
+ $csrfTokenValues = [];
+ }
+ // Save data
+ $csrfTokenValues[$tokenId] = ($this->_csrfTokenValue !== null) ? $this->_csrfTokenValue
+ : $csrfTokenValue;
+ if (count($csrfTokenValues) > $this->_csrfMaxTokenedPages) {
+ array_shift($csrfTokenValues);
+ }
+ A::app()->getSession()->set('token', $csrfTokenValues);
+ }
+ }
+
+ return $this->_csrfTokenValue;
+ }
+
+ /**
+ * Performs the CSRF validation
+ *
+ * @param string $formId
+ *
+ * @return void
+ */
+ public function validateCsrfToken($formId = '')
+ {
+ // Validate only POST requests
+ if ($this->isPostRequest()) {
+ $valid = false;
+ $tokenFromPost = isset($_POST[$this->_csrfTokenKey]) ? $_POST[$this->_csrfTokenKey] : null;
+
+ if ($this->_csrfTokenType == 'session') {
+ if (A::app()->getSession()->isExists('token') && isset($_POST[$this->_csrfTokenKey])) {
+ $tokenFromSession = A::app()->getSession()->get('token');
+ $valid = ($tokenFromSession === $tokenFromPost);
+ }
+ } elseif ($this->_csrfTokenType == 'cookie') {
+ if (A::app()->getCookie()->isExists('token') && isset($_POST[$this->_csrfTokenKey])) {
+ $tokenFromCookie = A::app()->getCookie()->get('token');
+ $valid = ($tokenFromCookie === $tokenFromPost);
+ }
+ } elseif ($this->_csrfTokenType == 'multipages' || $this->_csrfTokenType == 'multiforms') {
+ if ($this->_csrfTokenType == 'multipages') {
+ // Get page ID
+ $tokenId = $this->_getControllerAndAction();
+ } else {
+ $tokenId = $formId;
+ }
+ if (A::app()->getSession()->isExists('token') && isset($_POST[$this->_csrfTokenKey])) {
+ $tokenFromSession = A::app()->getSession()->get('token');
+ $tokenFromSession = isset($tokenFromSession[$tokenId]) ? $tokenFromSession[$tokenId] : '';
+ $valid = ($tokenFromSession === $tokenFromPost);
+ }
+ }
+
+ if ( ! $valid) {
+ unset($_POST);
+ A::app()->getSession()->setFlash(
+ 'csrfError',
+ CWidget::create(
+ 'CMessage',
+ [
+ 'warning',
+ A::t(
+ 'core',
+ 'The CSRF token has expired or invalid. Please try to
refresh page and resubmit the form.'
+ )
+ ]
+ )
+ );
+ CDebug::addMessage(
+ 'warnings',
+ 'csrf_token',
+ A::t('core', 'The CSRF token could not be verified.'),
+ 'session'
+ );
+ A::app()->getClientScript()->registerScript(
+ 'csrfError',
+ 'function csrf_refresh_page(){location.href = location.href + \'?\' + Math.random();}',
+ 2
+ );
+ }
+ }
+ }
+
+ /**
+ * Set GZIP compression handler
+ */
+ public function setGzipHandler()
+ {
+ if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')) {
+ // Fix for warning: ob_start() [ref.outcontrol]: output handler 'ob_gzhandler' conflicts with zlib output compression'
+ if (extension_loaded('zlib')) {
+ if (ob_get_length()) {
+ ob_end_clean();
+ }
+ }
+ ob_start('ob_gzhandler');
+ } else {
+ ob_start();
+ }
+ }
+
+ /**
+ * Cleans the request data
+ * This method removes slashes from request data if get_magic_quotes_gpc() is turned on
+ * Also performs CSRF validation if {@link _csrfValidation} is true
+ */
+ protected function _cleanRequest()
+ {
+ // 01.12.2018 - DEPRECATED
+ // Clean request only for PHP < 5.3, in greater versions of PHP 'magic' functions are deprecated
+ //if(version_compare(phpversion(), '5.3.0', '<')){
+ // if(function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()){
+ // $_GET = $this->stripSlashes($_GET);
+ // $_POST = $this->stripSlashes($_POST);
+ // $_REQUEST = $this->stripSlashes($_REQUEST);
+ // $_COOKIE = $this->stripSlashes($_COOKIE);
+ // }
+ //}
+
+ if ($this->getCsrfValidation()) {
+ A::app()->attachEventHandler('_onBeginRequest', [$this, 'validateCsrfToken']);
+ }
+ if ($this->_gzipCompression) {
+ A::app()->attachEventHandler('_onBeginRequest', [$this, 'setGzipHandler']);
+ }
+ if ($this->_referrerInSession) {
+ A::app()->attachEventHandler('_onBeginRequest', [$this, 'setHttpReferrer']);
+ }
+ }
+
+ /**
+ * Returns protocol and host
+ *
+ * @param bool $usePort
+ *
+ * @return string
+ */
+ protected function _getProtocolAndHost($usePort = true)
+ {
+ $protocol = 'http://';
+ $port = '';
+ $httpHost = isset($_SERVER['HTTP_HOST']) ? htmlentities($_SERVER['HTTP_HOST']) : '';
+ $serverProtocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : '';
+
+ if ((isset($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) != 'off'))
+ || strtolower(
+ substr($serverProtocol, 0, 5)
+ ) == 'https'
+ ) {
+ $protocol = 'https://';
+ }
+
+ if ($usePort) {
+ $portNumber = $this->getPort();
+ if ($portNumber != '80' && ! strpos($httpHost, ':')) {
+ $port = ':'.$portNumber;
+ }
+ }
+
+ return $protocol.$httpHost.$port;
+ }
+
+ /**
+ * Returns controller and action from URL
+ *
+ * @return string
+ */
+ protected function _getControllerAndAction()
+ {
+ $request = isset($_GET['url']) ? $_GET['url'] : '';
+ $split = explode('/', trim($request, '/'));
+ $pageId = isset($split[0]) ? $split[0] : '';
+ $pageId .= isset($split[1]) ? '_'.$split[1] : '';
+
+ return $pageId;
+ }
+
+ /**
+ * Returns parameter from global arrays $_GET or $_POST according to the type of request
+ *
+ * @param string $type
+ * @param string $name
+ * @param string|array $filters
+ * @param array $allowedValues
+ * @param string $default
+ *
+ * @return mixed
+ */
+ private function _getParam($type = 'get', $name = '', $default = '', $filters = '', $allowedValues = [])
+ {
+ $value = null;
+
+ if ($type == 'get') {
+ if (isset($_GET[$name])) {
+ $value = $_GET[$name];
+ } else {
+ // Check for variant
+ // URL: http://localhost/site/page/contact/param1/aaa/param2/bbb/param3/ccc
+ $request = isset($_GET['url']) ? $_GET['url'] : '';
+ $split = explode('/', trim($request, '/'));
+
+ $temp = [];
+ foreach ($split as $index => $part) {
+ if ( ! $temp || end($temp) !== null) {
+ $temp[$part] = null;
+ } else {
+ $arrayArg = array_keys($temp);
+ $tempEnd = end($arrayArg);
+ $temp[$tempEnd] = $part;
+ }
+ }
+ $temp = array_slice($temp, 1);
+ if (isset($temp[$name])) {
+ $value = $temp[$name];
+ }
+ }
+ } elseif ($type == 'post' && isset($_POST[$name])) {
+ $value = $_POST[$name];
+ } elseif ($type == 'request' && (isset($_GET[$name]) || isset($_POST[$name]))) {
+ $value = isset($_GET[$name]) ? $_GET[$name] : $_POST[$name];
+ }
+
+ if ($value !== null) {
+ // Validate allowed values
+ if ( ! empty($allowedValues)) {
+ if ( ! is_array($allowedValues)) {
+ $allowedValues = [$allowedValues];
+ }
+ if ( ! in_array($value, $allowedValues)) {
+ $value = $default;
+ }
+ }
+
+ // Filter values
+ if ( ! empty($filters)) {
+ if ( ! is_array($filters)) {
+ $filters = [$filters];
+ }
+ foreach ($filters as $filter) {
+ $value = CFilter::sanitize($filter, $value);
+ }
+ }
+ } else {
+ $value = $default;
+ }
+
+ return $value;
+ }
+
+ /**
+ * Returns global arrays: $_GET, $_POST or $_REQUEST according to given type
+ *
+ * @param string $type
+ *
+ * @return array
+ */
+ private function _getAll($type = 'get')
+ {
+ if ($type == 'get') {
+ return isset($_GET) ? $_GET : [];
+ } elseif ($type == 'post') {
+ return isset($_POST) ? $_POST : [];
+ } elseif ($type == 'request' && (isset($_GET) || isset($_POST))) {
+ return isset($_GET) ? $_GET : $_POST;
+ }
+
+ return [];
+ }
+
+ /**
+ * Downloads a content as file from browser to user
+ *
+ * @param string $fileName
+ * @param string $content
+ * @param string $mimeType
+ * @param boolean $terminate
+ */
+ public function downloadContent($fileName, $content, $mimeType = null, $terminate = true)
+ {
+ if ($mimeType === null) {
+ $mimeType = 'text/plain';
+ }
+
+ header('Pragma: public');
+ header('Expires: 0');
+ header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+ header('Content-type: '.$mimeType);
+ if (ob_get_length() === false) {
+ header('Content-Length: '.(function_exists('mb_strlen') ? mb_strlen($content, '8bit') : strlen($content)));
+ }
+ header('Content-Disposition: attachment; filename="'.$fileName.'"');
+ header('Content-Transfer-Encoding: binary');
+ echo $content;
+
+ if ($terminate) {
+ exit(0);
+ }
+ }
+
+ /**
+ * Downloads a file from browser to user
+ *
+ * @param string $file
+ * @param string $mimeType
+ * @param boolean $terminate
+ */
+ public function downloadFile($file, $mimeType = null, $terminate = true)
+ {
+ if ($mimeType === null) {
+ $mimeType = CFile::getMimeType($file);
+ }
+
+ header('Pragma: public');
+ header('Expires: 0');
+ header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+ header('Content-type: '.$mimeType);
+ header('Content-Length: '.filesize($file));
+ header('Content-Disposition: attachment; filename="'.basename($file).'"');
+ header('Content-Transfer-Encoding: binary');
+ readfile($file);
+
+ if ($terminate) {
+ exit(0);
+ }
+ }
+
+ /**
+ * Returns information about the browser of user
+ *
+ * @param string $key
+ * @param string $userAgent
+ *
+ * @return array
+ * @see http://www.php.net/manual/en/function.get-browser.php
+ */
+ public function getBrowser($key = '', $userAgent = null)
+ {
+ $browser = get_browser($userAgent, true);
+
+ if ( ! empty($key)) {
+ return isset($browser[$key]) ? $browser[$key] : '';
+ }
+
+ return $browser;
+ }
+
+ /**
+ * Sets HTTP Referrer
+ * Has potential risk because can insert current URL as a referrer and lead to endless loops on redirection
+ * Ex.: language or currency changes
+ */
+ public function setHttpReferrer()
+ {
+ // Save current data as previous referrer
+ A::app()->getSession()->set('http_referrer_previous', A::app()->getSession()->get('http_referrer_current'));
+ // Save current link as referrer
+ $httpReferrerCurrent = $this->_getProtocolAndHost().$this->getRequestUri();
+ A::app()->getSession()->set('http_referrer_current', $httpReferrerCurrent);
+ }
+
+ /**
+ * Returns the URL referrer, null if not present
+ */
+ public function getUrlReferrer()
+ {
+ $serverReferrer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : null;
+ $sessionReferrer = A::app()->getSession()->get('http_referrer_previous');
+
+ if ( ! empty($serverReferrer)) {
+ return $serverReferrer;
+ } elseif ( ! empty($sessionReferrer)) {
+ return $sessionReferrer;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the port to use for insecure requests
+ * Defaults to 80 or the port specified by the server (if the current request is insecure)
+ *
+ * @return int
+ * @since 0.7.0
+ */
+ public function getPort()
+ {
+ if ($this->_port === null) {
+ $this->_port = ! $this->isSecureConnection() && isset($_SERVER['SERVER_PORT'])
+ ? (int)$_SERVER['SERVER_PORT'] : 80;
+ }
+
+ return $this->_port;
+ }
+
+ /**
+ * Returns the port to use for secure requests
+ * Defaults to 443, or the port specified by the server (if the current request is secure)
+ *
+ * @return int
+ * @since 0.7.0
+ */
+ public function getSecurePort()
+ {
+ if ($this->_securePort === null) {
+ $this->_securePort = $this->isSecureConnection() && isset($_SERVER['SERVER_PORT'])
+ ? (int)$_SERVER['SERVER_PORT'] : 443;
+ }
+
+ return $this->_securePort;
+ }
+
+ /**
+ * Returns content of the given URL
+ *
+ * @param string $url
+ * @param string $method
+ * @param string $data
+ * @param string $params
+ * @param string $function 'file_get_contents' or 'curl'
+ *
+ * @return mixed
+ */
+ function getUrlContent($url = '', $method = 'get', $data = [], $params = [], $function = 'file_get_contents')
+ {
+ # Validate function argumanets
+ $method = strtolower($method);
+ $data = (array)$data;
+
+ if (empty($url) && ! in_array($method, ['get', 'post'])) {
+ return true;
+ }
+
+ # Get parameters
+ $ajaxCall = isset($params['ajax']) ? (bool)$params['ajax'] : false;
+ $showErrors = isset($params['errors']) ? (bool)$params['errors'] : false;
+ $json = isset($params['json']) ? (bool)$params['json'] : false;
+ $sslVerifyHost = isset($params['ssl_verify_host']) ? (bool)$params['ssl_verify_host'] : false;
+ $sslVerifyPeer = isset($params['ssl_verify_peer']) ? (bool)$params['ssl_verify_peer'] : false;
+ $result = null;
+
+ if ($function == 'curl') {
+ # Init curl
+ $ch = curl_init();
+
+ # Set options
+ curl_setopt($ch, CURLOPT_URL, $url);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0);
+ curl_setopt(
+ $ch,
+ CURLOPT_USERAGENT,
+ 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322)'
+ );
+
+ # Fake AJAX call
+ if ($ajaxCall) {
+ curl_setopt($ch, CURLOPT_HTTPHEADER, ["X-Requested-With: XMLHttpRequest"]);
+ }
+
+ # SSL verification
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, ($sslVerifyHost ? 2 : 0));
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, ($sslVerifyPeer ? 1 : 0));
+
+ if ($method == 'post') {
+ # Set the HEADER, number of POST vars, POST data
+ if ($json) {
+ curl_setopt($ch, CURLOPT_HEADER, false);
+ curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-type: application/json"]);
+ curl_setopt($ch, CURLOPT_POST, count($data));
+ curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
+ } else {
+ curl_setopt($ch, CURLOPT_POST, count($data));
+ curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
+ curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
+ }
+ }
+
+ if ($showErrors) {
+ # Check for errors and include in the error message
+ $error = '';
+ if ($errno = curl_errno($ch)) {
+ $errorMessage = function_exists('curl_strerror') ? curl_strerror($errno) : '';
+ $error = "cURL error ({$errno}):\n {$errorMessage}";
+ }
+
+ $result['result'] = curl_exec($ch);
+ $result['error'] = $error;
+ } else {
+ $result = curl_exec($ch);
+ }
+
+ # Close connection
+ curl_close($ch);
+ } else {
+ $context = null;
+
+ # Use key 'http' even if you send the request to https://
+ if ($method == 'post') {
+ $options = [
+ 'http' => [
+ 'header' => "Content-type: application/x-www-form-urlencoded\r\n".
+ ($ajaxCall ? "X-Requested-With: XMLHttpRequest\r\n" : ''),
+ 'method' => 'POST',
+ 'content' => http_build_query($data),
+ ],
+ ];
+
+ # Disable SSL verification
+ if ( ! $sslVerifyPeer) {
+ $options['ssl'] = [
+ 'verify_peer' => false,
+ 'verify_peer_name' => false,
+ ];
+ }
+
+ $context = stream_context_create($options);
+ }
+
+ $result = file_get_contents($url, false, $context);
+ }
+
+ return $result;
+ }
+
}
diff --git a/framework/components/CHttpSession.php b/framework/components/CHttpSession.php
index 37b087d..5514c92 100644
--- a/framework/components/CHttpSession.php
+++ b/framework/components/CHttpSession.php
@@ -35,280 +35,313 @@
class CHttpSession extends CComponent
{
-
- /** @var boolean */
- protected $_autoStart = true;
- /** @var string */
- protected $_defaultSessionName = 'apphp_framework';
- /** @var string */
- protected $_defaultSessionPrefix = 'apphp_';
- /**
- * @var int
- * @deprecated since v0.1.0
- * 0 - use name prefix, 1 - use session name (default)
- */
- protected $_multiSiteSupportType = 1;
- /**
- * @var mixed
- */
- protected $_prefix = '';
- /**
- * @var string
- * only | allow | none
- */
- protected $_cookieMode = 'allow';
-
-
- /**
- * Class default constructor
- */
- function __construct()
- {
- if ($this->_cookieMode !== 'only') {
- $this->_setCookieMode($this->_cookieMode);
- }
-
- if ($this->_multiSiteSupportType) {
- $this->setSessionName('apphp_' . CConfig::get('installationKey'));
- } else {
- $this->setSessionPrefix('apphp_' . CConfig::get('installationKey'));
- }
-
- if ($this->_autoStart) $this->startSession();
- }
-
- /**
- * Returns the instance of object
- * @return current class
- */
- public static function init()
- {
- return parent::init(__CLASS__);
- }
-
- /**
- * Sets session variable
- * @param string $name
- * @param mixed $value
- */
- public function set($name, $value)
- {
- $_SESSION[$this->_prefix . $name] = $value;
- }
-
- /**
- * Returns session variable
- * @param string $name
- * @param mixed $default
- * @return mixed
- */
- public function get($name, $default = '')
- {
- return isset($_SESSION[$this->_prefix . $name]) ? $_SESSION[$this->_prefix . $name] : $default;
- }
-
- /**
- * Returns all session variables
- * @return mixed
- */
- public function getAll()
- {
- return isset($_SESSION) ? $_SESSION : null;
- }
-
- /**
- * Removes session variable
- * @param string $name
- */
- public function remove($name)
- {
- if (isset($_SESSION[$this->_prefix . $name])) {
- unset($_SESSION[$this->_prefix . $name]);
- return true;
- }
-
- return false;
- }
-
- /**
- * Removes all session variable
- * @return void
- */
- public function removeAll()
- {
- @session_unset();
- if (is_array($_SESSION)) {
- foreach ($_SESSION as $key => $val) {
- unset($_SESSION[$key]);
- }
- }
- }
-
- /**
- * Checks if session variable exists
- * @param string $name
- */
- public function isExists($name)
- {
- return isset($_SESSION[$this->_prefix . $name]) ? true : false;
- }
-
- /**
- * Sets session flash data
- * @param string $name
- * @param mixed $value
- */
- public function setFlash($name, $value)
- {
- $_SESSION[$this->_prefix . '_flash'][$name] = $value;
- }
-
- /**
- * Returns session flash data
- * @param string $name
- * @param mixed $default
- */
- public function getFlash($name, $default = '')
- {
- if (isset($_SESSION[$this->_prefix . '_flash'][$name])) {
- $result = $_SESSION[$this->_prefix . '_flash'][$name];
- unset($_SESSION[$this->_prefix . '_flash'][$name]);
- } else {
- $result = $default;
- }
- return $result;
- }
-
- /**
- * Checks if has flash data
- * @param string $name
- * @return bool
- */
- public function hasFlash($name)
- {
- return isset($_SESSION[$this->_prefix . '_flash'][$name]) ? true : false;
- }
-
- /**
- * Sets session name
- * @param string $value
- */
- public function setSessionName($value)
- {
- if (empty($value)) $value = $this->_defaultSessionName;
- session_name($value);
- }
-
- /**
- * Sets session name
- * @param string $value
- */
- public function setSessionPrefix($value)
- {
- if (empty($value)) $value = $this->_defaultSessionPrefix;
- $this->_prefix = $value;
- }
-
- /**
- * Gets session name
- * @return string
- */
- public function getSessionName()
- {
- return session_name();
- }
-
- /**
- * Sets the number of seconds after which data will be seen as 'garbage' and cleaned up
- * @param int $value
- */
- public function setTimeout($value)
- {
- ini_set('session.gc_maxlifetime', (int)$value);
- }
-
- /**
- * Returns the number of seconds after which data will be seen as 'garbage' and cleaned up
- * @return integer
- */
- public function getTimeout()
- {
- // Get lifetime value from configuration file (in minutes)
- $maxlifetime = CConfig::get('session.lifetime');
- return (!empty($maxlifetime)) ? (int)($maxlifetime * 60) : (int)ini_get('session.gc_maxlifetime');
- }
-
- /**
- * Destroys the session
- */
- public function endSession()
- {
- if (session_id() !== '') {
- @session_unset();
- @session_destroy();
- }
- }
-
- /**
- * Gets cookie mode
- * @return string
- */
- public function getCookieMode()
- {
- if (ini_get('session.use_cookies') === '0') {
- return 'none';
- } elseif (ini_get('session.use_only_cookies') === '0') {
- return 'allow';
- } else {
- return 'only';
- }
- }
-
- /**
- * Session close handler
- * Do not call this method directly
- * @return boolean
- */
- public function closeSession()
- {
- return true;
- }
-
- /**
- * Starts the session if it has not started yet
- */
- public function startSession()
- {
- // Set lifetime value from configuration file (in minutes)
- $maxLifetime = CConfig::get('session.lifetime');
- if (!empty($maxLifetime) && $maxLifetime != ini_get('session.gc_maxlifetime')) {
- $this->setTimeout($maxLifetime);
- }
-
- @session_start();
- if (APPHP_MODE == 'debug' && session_id() == '') {
- CDebug::addMessage('errors', 'session', A::t('core', 'Failed to start session'));
- }
- }
-
- /**
- * Sets cookie mode
- * @value string
- */
- private function _setCookieMode($value = '')
- {
- if ($value === 'none') {
- ini_set('session.use_cookies', '0');
- ini_set('session.use_only_cookies', '0');
- } elseif ($value === 'allow') {
- ini_set('session.use_cookies', '1');
- ini_set('session.use_only_cookies', '0');
- } elseif ($value === 'only') {
- ini_set('session.use_cookies', '1');
- ini_set('session.use_only_cookies', '1');
- } else {
- CDebug::addMessage('warnings', 'session_cookie_mode', A::t('core', 'HttpSession.cookieMode can only be "none", "allow" or "only".'));
- }
- }
-
+
+ /** @var boolean */
+ protected $_autoStart = true;
+ /** @var string */
+ protected $_defaultSessionName = 'apphp_framework';
+ /** @var string */
+ protected $_defaultSessionPrefix = 'apphp_';
+ /**
+ * @var int
+ * @deprecated since v0.1.0
+ * 0 - use name prefix, 1 - use session name (default)
+ */
+ protected $_multiSiteSupportType = 1;
+ /**
+ * @var mixed
+ */
+ protected $_prefix = '';
+ /**
+ * @var string
+ * only | allow | none
+ */
+ protected $_cookieMode = 'allow';
+
+
+ /**
+ * Class default constructor
+ */
+ function __construct()
+ {
+ if ($this->_cookieMode !== 'only') {
+ $this->_setCookieMode($this->_cookieMode);
+ }
+
+ if ($this->_multiSiteSupportType) {
+ $this->setSessionName('apphp_'.CConfig::get('installationKey'));
+ } else {
+ $this->setSessionPrefix('apphp_'.CConfig::get('installationKey'));
+ }
+
+ if ($this->_autoStart) {
+ $this->startSession();
+ }
+ }
+
+ /**
+ * Returns the instance of object
+ *
+ * @return current class
+ */
+ public static function init()
+ {
+ return parent::init(__CLASS__);
+ }
+
+ /**
+ * Sets session variable
+ *
+ * @param string $name
+ * @param mixed $value
+ */
+ public function set($name, $value)
+ {
+ $_SESSION[$this->_prefix.$name] = $value;
+ }
+
+ /**
+ * Returns session variable
+ *
+ * @param string $name
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function get($name, $default = '')
+ {
+ return isset($_SESSION[$this->_prefix.$name]) ? $_SESSION[$this->_prefix.$name] : $default;
+ }
+
+ /**
+ * Returns all session variables
+ *
+ * @return mixed
+ */
+ public function getAll()
+ {
+ return isset($_SESSION) ? $_SESSION : null;
+ }
+
+ /**
+ * Removes session variable
+ *
+ * @param string $name
+ */
+ public function remove($name)
+ {
+ if (isset($_SESSION[$this->_prefix.$name])) {
+ unset($_SESSION[$this->_prefix.$name]);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Removes all session variable
+ *
+ * @return void
+ */
+ public function removeAll()
+ {
+ @session_unset();
+ if (is_array($_SESSION)) {
+ foreach ($_SESSION as $key => $val) {
+ unset($_SESSION[$key]);
+ }
+ }
+ }
+
+ /**
+ * Checks if session variable exists
+ *
+ * @param string $name
+ */
+ public function isExists($name)
+ {
+ return isset($_SESSION[$this->_prefix.$name]) ? true : false;
+ }
+
+ /**
+ * Sets session flash data
+ *
+ * @param string $name
+ * @param mixed $value
+ */
+ public function setFlash($name, $value)
+ {
+ $_SESSION[$this->_prefix.'_flash'][$name] = $value;
+ }
+
+ /**
+ * Returns session flash data
+ *
+ * @param string $name
+ * @param mixed $default
+ */
+ public function getFlash($name, $default = '')
+ {
+ if (isset($_SESSION[$this->_prefix.'_flash'][$name])) {
+ $result = $_SESSION[$this->_prefix.'_flash'][$name];
+ unset($_SESSION[$this->_prefix.'_flash'][$name]);
+ } else {
+ $result = $default;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Checks if has flash data
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ public function hasFlash($name)
+ {
+ return isset($_SESSION[$this->_prefix.'_flash'][$name]) ? true : false;
+ }
+
+ /**
+ * Sets session name
+ *
+ * @param string $value
+ */
+ public function setSessionName($value)
+ {
+ if (empty($value)) {
+ $value = $this->_defaultSessionName;
+ }
+ session_name($value);
+ }
+
+ /**
+ * Sets session name
+ *
+ * @param string $value
+ */
+ public function setSessionPrefix($value)
+ {
+ if (empty($value)) {
+ $value = $this->_defaultSessionPrefix;
+ }
+ $this->_prefix = $value;
+ }
+
+ /**
+ * Gets session name
+ *
+ * @return string
+ */
+ public function getSessionName()
+ {
+ return session_name();
+ }
+
+ /**
+ * Sets the number of seconds after which data will be seen as 'garbage' and cleaned up
+ *
+ * @param int $value
+ */
+ public function setTimeout($value)
+ {
+ ini_set('session.gc_maxlifetime', (int)$value);
+ }
+
+ /**
+ * Returns the number of seconds after which data will be seen as 'garbage' and cleaned up
+ *
+ * @return integer
+ */
+ public function getTimeout()
+ {
+ // Get lifetime value from configuration file (in minutes)
+ $maxlifetime = CConfig::get('session.lifetime');
+
+ return ( ! empty($maxlifetime)) ? (int)($maxlifetime * 60) : (int)ini_get('session.gc_maxlifetime');
+ }
+
+ /**
+ * Destroys the session
+ */
+ public function endSession()
+ {
+ if (session_id() !== '') {
+ @session_unset();
+ @session_destroy();
+ }
+ }
+
+ /**
+ * Gets cookie mode
+ *
+ * @return string
+ */
+ public function getCookieMode()
+ {
+ if (ini_get('session.use_cookies') === '0') {
+ return 'none';
+ } elseif (ini_get('session.use_only_cookies') === '0') {
+ return 'allow';
+ } else {
+ return 'only';
+ }
+ }
+
+ /**
+ * Session close handler
+ * Do not call this method directly
+ *
+ * @return boolean
+ */
+ public function closeSession()
+ {
+ return true;
+ }
+
+ /**
+ * Starts the session if it has not started yet
+ */
+ public function startSession()
+ {
+ // Set lifetime value from configuration file (in minutes)
+ $maxLifetime = CConfig::get('session.lifetime');
+ if ( ! empty($maxLifetime) && $maxLifetime != ini_get('session.gc_maxlifetime')) {
+ $this->setTimeout($maxLifetime);
+ }
+
+ @session_start();
+ if (APPHP_MODE == 'debug' && session_id() == '') {
+ CDebug::addMessage('errors', 'session', A::t('core', 'Failed to start session'));
+ }
+ }
+
+ /**
+ * Sets cookie mode
+ *
+ * @value string
+ */
+ private function _setCookieMode($value = '')
+ {
+ if ($value === 'none') {
+ ini_set('session.use_cookies', '0');
+ ini_set('session.use_only_cookies', '0');
+ } elseif ($value === 'allow') {
+ ini_set('session.use_cookies', '1');
+ ini_set('session.use_only_cookies', '0');
+ } elseif ($value === 'only') {
+ ini_set('session.use_cookies', '1');
+ ini_set('session.use_only_cookies', '1');
+ } else {
+ CDebug::addMessage(
+ 'warnings',
+ 'session_cookie_mode',
+ A::t('core', 'HttpSession.cookieMode can only be "none", "allow" or "only".')
+ );
+ }
+ }
+
}
\ No newline at end of file
diff --git a/framework/components/CLocalTime.php b/framework/components/CLocalTime.php
index f901e94..8bfd7fb 100644
--- a/framework/components/CLocalTime.php
+++ b/framework/components/CLocalTime.php
@@ -22,380 +22,902 @@
class CLocalTime extends CComponent
{
-
- /**
- * Class default constructor
- */
- function __construct()
- {
-
- }
-
- /**
- * Returns the instance of object
- * @return current class
- */
- public static function init()
- {
- return parent::init(__CLASS__);
- }
-
- /**
- * Sets the time zone used by the application
- * @param string $value
- * @see http://php.net/manual/en/function.date-default-timezone-set.php
- */
- public function setTimeZone($value)
- {
- date_default_timezone_set($value);
- }
-
- /**
- * Returns the time zone used by the application
- * @return string
- * @see http://php.net/manual/en/function.date-default-timezone-set.php
- */
- public function getTimeZone()
- {
- return date_default_timezone_get();
- }
-
- /**
- * Returns a list of locales
- * @return array
- */
- public function getLocales()
- {
- return array(
- 'sq_AL' => A::t('i18n', 'languages.sq') . ' - ' . A::t('i18n', 'countries.al'),
- 'ar_AE' => A::t('i18n', 'languages.ar') . ' - ' . A::t('i18n', 'countries.ae'),
- 'ar_BH' => A::t('i18n', 'languages.ar') . ' - ' . A::t('i18n', 'countries.bh'),
- 'ar_DZ' => A::t('i18n', 'languages.ar') . ' - ' . A::t('i18n', 'countries.dz'),
- 'ar_EG' => A::t('i18n', 'languages.ar') . ' - ' . A::t('i18n', 'countries.eg'),
- 'ar_IN' => A::t('i18n', 'languages.ar') . ' - ' . A::t('i18n', 'countries.in'),
- 'ar_IQ' => A::t('i18n', 'languages.ar') . ' - ' . A::t('i18n', 'countries.iq'),
- 'ar_JO' => A::t('i18n', 'languages.ar') . ' - ' . A::t('i18n', 'countries.jo'),
- 'ar_KW' => A::t('i18n', 'languages.ar') . ' - ' . A::t('i18n', 'countries.kw'),
- 'ar_LB' => A::t('i18n', 'languages.ar') . ' - ' . A::t('i18n', 'countries.lb'),
- 'ar_LY' => A::t('i18n', 'languages.ar') . ' - ' . A::t('i18n', 'countries.ly'),
- 'ar_MA' => A::t('i18n', 'languages.ar') . ' - ' . A::t('i18n', 'countries.ma'),
- 'ar_OM' => A::t('i18n', 'languages.ar') . ' - ' . A::t('i18n', 'countries.om'),
- 'ar_QA' => A::t('i18n', 'languages.ar') . ' - ' . A::t('i18n', 'countries.qa'),
- 'ar_SA' => A::t('i18n', 'languages.ar') . ' - ' . A::t('i18n', 'countries.sa'),
- 'ar_SD' => A::t('i18n', 'languages.ar') . ' - ' . A::t('i18n', 'countries.sd'),
- 'ar_SY' => A::t('i18n', 'languages.ar') . ' - ' . A::t('i18n', 'countries.sy'),
- 'ar_TN' => A::t('i18n', 'languages.ar') . ' - ' . A::t('i18n', 'countries.tn'),
- 'ar_YE' => A::t('i18n', 'languages.ar') . ' - ' . A::t('i18n', 'countries.ye'),
- 'eu_ES' => A::t('i18n', 'languages.eu') . ' - ' . A::t('i18n', 'countries.es'),
- 'be_BY' => A::t('i18n', 'languages.be') . ' - ' . A::t('i18n', 'countries.by'),
- 'bg_BG' => A::t('i18n', 'languages.bg') . ' - ' . A::t('i18n', 'countries.bg'),
- 'ca_ES' => A::t('i18n', 'languages.ca') . ' - ' . A::t('i18n', 'countries.es'),
- 'zh_CN' => A::t('i18n', 'languages.zh') . ' - ' . A::t('i18n', 'countries.cn'),
- 'zh_HK' => A::t('i18n', 'languages.zh') . ' - ' . A::t('i18n', 'countries.hk'),
- 'zh_TW' => A::t('i18n', 'languages.zh') . ' - ' . A::t('i18n', 'countries.tw'),
- 'hr_HR' => A::t('i18n', 'languages.hr') . ' - ' . A::t('i18n', 'countries.hr'),
- 'cs_CZ' => A::t('i18n', 'languages.cs') . ' - ' . A::t('i18n', 'countries.cz'),
- 'da_DK' => A::t('i18n', 'languages.da') . ' - ' . A::t('i18n', 'countries.dk'),
- 'nl_BE' => A::t('i18n', 'languages.nl') . ' - ' . A::t('i18n', 'countries.be'),
- 'nl_NL' => A::t('i18n', 'languages.nl') . ' - ' . A::t('i18n', 'countries.nl'),
- 'de_AT' => A::t('i18n', 'languages.de') . ' - ' . A::t('i18n', 'countries.at'),
- 'de_BE' => A::t('i18n', 'languages.de') . ' - ' . A::t('i18n', 'countries.be'),
- 'de_CH' => A::t('i18n', 'languages.de') . ' - ' . A::t('i18n', 'countries.ch'),
- 'de_DE' => A::t('i18n', 'languages.de') . ' - ' . A::t('i18n', 'countries.de'),
- 'de_LU' => A::t('i18n', 'languages.de') . ' - ' . A::t('i18n', 'countries.lu'),
- 'en_AU' => A::t('i18n', 'languages.en') . ' - ' . A::t('i18n', 'countries.au'),
- 'en_CA' => A::t('i18n', 'languages.en') . ' - ' . A::t('i18n', 'countries.ca'),
- 'en_GB' => A::t('i18n', 'languages.en') . ' - ' . A::t('i18n', 'countries.gb'),
- 'en_IN' => A::t('i18n', 'languages.en') . ' - ' . A::t('i18n', 'countries.in'),
- 'en_NZ' => A::t('i18n', 'languages.en') . ' - ' . A::t('i18n', 'countries.nz'),
- 'en_PH' => A::t('i18n', 'languages.en') . ' - ' . A::t('i18n', 'countries.ph'),
- 'en_US' => A::t('i18n', 'languages.en') . ' - ' . A::t('i18n', 'countries.us'),
- 'en_ZA' => A::t('i18n', 'languages.en') . ' - ' . A::t('i18n', 'countries.za'),
- 'en_ZW' => A::t('i18n', 'languages.en') . ' - ' . A::t('i18n', 'countries.zw'),
- 'et_EE' => A::t('i18n', 'languages.et') . ' - ' . A::t('i18n', 'countries.ee'),
- 'fi_FI' => A::t('i18n', 'languages.fi') . ' - ' . A::t('i18n', 'countries.fi'),
- 'fo_FO' => A::t('i18n', 'languages.fo') . ' - ' . A::t('i18n', 'countries.fo'),
- 'fr_BE' => A::t('i18n', 'languages.fr') . ' - ' . A::t('i18n', 'countries.be'),
- 'fr_CA' => A::t('i18n', 'languages.fr') . ' - ' . A::t('i18n', 'countries.ca'),
- 'fr_CH' => A::t('i18n', 'languages.fr') . ' - ' . A::t('i18n', 'countries.ch'),
- 'fr_FR' => A::t('i18n', 'languages.fr') . ' - ' . A::t('i18n', 'countries.fr'),
- 'fr_LU' => A::t('i18n', 'languages.fr') . ' - ' . A::t('i18n', 'countries.lu'),
- 'gl_ES' => A::t('i18n', 'languages.gl') . ' - ' . A::t('i18n', 'countries.es'),
- 'el_GR' => A::t('i18n', 'languages.el') . ' - ' . A::t('i18n', 'countries.gr'),
- 'gu_IN' => A::t('i18n', 'languages.gu') . ' - ' . A::t('i18n', 'countries.in'),
- 'he_IL' => A::t('i18n', 'languages.he') . ' - ' . A::t('i18n', 'countries.il'),
- 'hi_IN' => A::t('i18n', 'languages.hi') . ' - ' . A::t('i18n', 'countries.in'),
- 'hu_HU' => A::t('i18n', 'languages.hu') . ' - ' . A::t('i18n', 'countries.hu'),
- 'id_ID' => A::t('i18n', 'languages.id') . ' - ' . A::t('i18n', 'countries.id'),
- 'is_IS' => A::t('i18n', 'languages.is') . ' - ' . A::t('i18n', 'countries.is'),
- 'it_CH' => A::t('i18n', 'languages.it') . ' - ' . A::t('i18n', 'countries.ch'),
- 'it_IT' => A::t('i18n', 'languages.it') . ' - ' . A::t('i18n', 'countries.it'),
- 'ja_JP' => A::t('i18n', 'languages.ja') . ' - ' . A::t('i18n', 'countries.jp'),
- 'ko_KR' => A::t('i18n', 'languages.ko') . ' - ' . A::t('i18n', 'countries.kr'),
- 'lt_LT' => A::t('i18n', 'languages.lt') . ' - ' . A::t('i18n', 'countries.lt'),
- 'lv_LV' => A::t('i18n', 'languages.lv') . ' - ' . A::t('i18n', 'countries.lv'),
- 'mk_MK' => A::t('i18n', 'languages.mk') . ' - ' . A::t('i18n', 'countries.mk'),
- 'mn_MN' => A::t('i18n', 'languages.mn') . ' - ' . A::t('i18n', 'countries.mn'),
- 'ms_MY' => A::t('i18n', 'languages.ms') . ' - ' . A::t('i18n', 'countries.my'),
- 'nb_NO' => A::t('i18n', 'languages.nb') . ' - ' . A::t('i18n', 'countries.no'),
- 'no_NO' => A::t('i18n', 'languages.no') . ' - ' . A::t('i18n', 'countries.no'),
- 'pl_PL' => A::t('i18n', 'languages.pl') . ' - ' . A::t('i18n', 'countries.pl'),
- 'pt_BR' => A::t('i18n', 'languages.pt') . ' - ' . A::t('i18n', 'countries.br'),
- 'pt_PT' => A::t('i18n', 'languages.pt') . ' - ' . A::t('i18n', 'countries.pt'),
- 'ro_RO' => A::t('i18n', 'languages.ro') . ' - ' . A::t('i18n', 'countries.ro'),
- 'ru_RU' => A::t('i18n', 'languages.ru') . ' - ' . A::t('i18n', 'countries.ru'),
- 'ru_UA' => A::t('i18n', 'languages.ru') . ' - ' . A::t('i18n', 'countries.ua'),
- 'sk_SK' => A::t('i18n', 'languages.sk') . ' - ' . A::t('i18n', 'countries.sk'),
- 'sl_SI' => A::t('i18n', 'languages.sl') . ' - ' . A::t('i18n', 'countries.si'),
- 'sr_YU' => A::t('i18n', 'languages.sr') . ' - ' . A::t('i18n', 'countries.rs'),
- 'es_AR' => A::t('i18n', 'languages.es') . ' - ' . A::t('i18n', 'countries.ar'),
- 'es_BO' => A::t('i18n', 'languages.es') . ' - ' . A::t('i18n', 'countries.bo'),
- 'es_CL' => A::t('i18n', 'languages.es') . ' - ' . A::t('i18n', 'countries.cl'),
- 'es_CO' => A::t('i18n', 'languages.es') . ' - ' . A::t('i18n', 'countries.co'),
- 'es_CR' => A::t('i18n', 'languages.es') . ' - ' . A::t('i18n', 'countries.cr'),
- 'es_DO' => A::t('i18n', 'languages.es') . ' - ' . A::t('i18n', 'countries.do'),
- 'es_EC' => A::t('i18n', 'languages.es') . ' - ' . A::t('i18n', 'countries.ec'),
- 'es_ES' => A::t('i18n', 'languages.es') . ' - ' . A::t('i18n', 'countries.es'),
- 'es_GT' => A::t('i18n', 'languages.es') . ' - ' . A::t('i18n', 'countries.gt'),
- 'es_HN' => A::t('i18n', 'languages.es') . ' - ' . A::t('i18n', 'countries.hn'),
- 'es_MX' => A::t('i18n', 'languages.es') . ' - ' . A::t('i18n', 'countries.mx'),
- 'es_NI' => A::t('i18n', 'languages.es') . ' - ' . A::t('i18n', 'countries.ni'),
- 'es_PA' => A::t('i18n', 'languages.es') . ' - ' . A::t('i18n', 'countries.pa'),
- 'es_PE' => A::t('i18n', 'languages.es') . ' - ' . A::t('i18n', 'countries.pe'),
- 'es_PR' => A::t('i18n', 'languages.es') . ' - ' . A::t('i18n', 'countries.pr'),
- 'es_PY' => A::t('i18n', 'languages.es') . ' - ' . A::t('i18n', 'countries.py'),
- 'es_SV' => A::t('i18n', 'languages.es') . ' - ' . A::t('i18n', 'countries.sv'),
- 'es_US' => A::t('i18n', 'languages.es') . ' - ' . A::t('i18n', 'countries.us'),
- 'es_UY' => A::t('i18n', 'languages.es') . ' - ' . A::t('i18n', 'countries.uy'),
- 'es_VE' => A::t('i18n', 'languages.es') . ' - ' . A::t('i18n', 'countries.ve'),
- 'sv_FI' => A::t('i18n', 'languages.sv') . ' - ' . A::t('i18n', 'countries.fi'),
- 'sv_SE' => A::t('i18n', 'languages.sv') . ' - ' . A::t('i18n', 'countries.se'),
- 'ta_IN' => A::t('i18n', 'languages.ta') . ' - ' . A::t('i18n', 'countries.in'),
- 'te_IN' => A::t('i18n', 'languages.te') . ' - ' . A::t('i18n', 'countries.in'),
- 'th_TH' => A::t('i18n', 'languages.th') . ' - ' . A::t('i18n', 'countries.th'),
- 'tr_TR' => A::t('i18n', 'languages.tr') . ' - ' . A::t('i18n', 'countries.tr'),
- 'uk_UA' => A::t('i18n', 'languages.uk') . ' - ' . A::t('i18n', 'countries.ua'),
- 'ur_PK' => A::t('i18n', 'languages.ur') . ' - ' . A::t('i18n', 'countries.pk'),
- 'vi_VN' => A::t('i18n', 'languages.vi') . ' - ' . A::t('i18n', 'countries.vn'),
- );
- }
-
- /**
- * Returns a timzone offset or full name by time zone name
- * @param string $name
- * @param string $type 'offset' - default, 'offset_name' or 'full_name'
- * @return float|string
- */
- public function getTimeZoneInfo($name = '', $type = 'offset')
- {
- $return = '';
-
- if (!empty($name)) {
- $timeZones = $this->getTimeZones();
- foreach ($timeZones as $timeZone) {
- foreach ($timeZone as $zoneName => $zoneInfo) {
- if (strtolower($zoneName) == strtolower($name)) {
- if ($type == 'offset') {
- $return = isset($zoneInfo['offset']) ? $zoneInfo['offset'] : '';
- } elseif ($type == 'offset_name') {
- $return = (isset($zoneInfo['offset_text']) && isset($zoneInfo['name'])) ? $zoneInfo['offset_text'] : '';
- } elseif ($type == 'full_name') {
- $return = (isset($zoneInfo['offset_text']) && isset($zoneInfo['name'])) ? $zoneInfo['offset_text'] . ' ' . $zoneInfo['name'] : '';
- }
- break(2);
- }
- }
- }
- }
-
- return $return;
- }
-
- /**
- * Returns a nested array of timzones by continents
- * @return array
- */
- public function getTimeZones()
- {
- return array(
- 'Africa' => array(
- 'Africa/Casablanca' => array('offset' => '0', 'offset_text' => '[GMT+00:00]', 'name' => 'Western European Time (Africa/ Casablanca)'),
- 'Africa/Algiers' => array('offset' => '1', 'offset_text' => '[GMT+01:00]', 'name' => 'Central European Time (Africa/ Algiers)'),
- 'Africa/Bangui' => array('offset' => '1', 'offset_text' => '[GMT+01:00]', 'name' => 'Western African Time (Africa/ Bangui)'),
- 'Africa/Windhoek' => array('offset' => '1', 'offset_text' => '[GMT+01:00]', 'name' => 'Western African Time (Africa/ Windhoek)'),
- 'Africa/Tripoli' => array('offset' => '2', 'offset_text' => '[GMT+02:00]', 'name' => 'Eastern European Time (Africa/ Tripoli)'),
- 'Africa/Johannesburg' => array('offset' => '2', 'offset_text' => '[GMT+02:00]', 'name' => 'South Africa Standard Time (Africa/ Johannesburg)'),
- 'Africa/Dar_es_Salaam' => array('offset' => '3', 'offset_text' => '[GMT+03:00]', 'name' => 'Eastern African Time (EAT)'),
- ),
- 'America (North & South)' => array(
- 'America/Scoresbysund' => array('offset' => '-1', 'offset_text' => '[GMT-01:00]', 'name' => 'Eastern Greenland Time (America/ Scoresbysund)'),
- 'America/Noronha' => array('offset' => '-2', 'offset_text' => '[GMT-02:00]', 'name' => 'Fernando de Noronha Time (America/ Noronha)'),
- 'America/Argentina/Buenos_Aires' => array('offset' => '-3', 'offset_text' => '[GMT-03:00]', 'name' => 'Argentine Time (AGT)'),
- 'America/Belem' => array('offset' => '-3', 'offset_text' => '[GMT-03:00]', 'name' => 'Brazil Time (America/ Belem)'),
- 'America/Sao_Paulo' => array('offset' => '-3', 'offset_text' => '[GMT-03:00]', 'name' => 'Brazil Time (BET)'),
- 'America/Cayenne' => array('offset' => '-3', 'offset_text' => '[GMT-03:00]', 'name' => 'French Guiana Time (America/ Cayenne)'),
- 'America/Miquelon' => array('offset' => '-3', 'offset_text' => '[GMT-03:00]', 'name' => 'Pierre & Miquelon Standard Time (America/ Miquelon)'),
- 'America/Paramaribo' => array('offset' => '-3', 'offset_text' => '[GMT-03:00]', 'name' => 'Suriname Time (America/ Paramaribo)'),
- 'America/Montevideo' => array('offset' => '-3', 'offset_text' => '[GMT-03:00]', 'name' => 'Uruguay Time (America/ Montevideo)'),
- 'America/Godthab' => array('offset' => '-3', 'offset_text' => '[GMT-03:00]', 'name' => 'Western Greenland Time (America/ Godthab)'),
- 'America/St_Johns' => array('offset' => '-3', 'offset_text' => '[GMT-03:30]', 'name' => 'Newfoundland Standard Time (America/ St Johns)'),
- 'America/Cuiaba' => array('offset' => '-4', 'offset_text' => '[GMT-04:00]', 'name' => 'Amazon Standard Time (America/ Cuiaba)'),
- 'America/Glace_Bay' => array('offset' => '-4', 'offset_text' => '[GMT-04:00]', 'name' => 'Atlantic Standard Time (America/ Glace Bay)'),
- 'America/La_Paz' => array('offset' => '-4', 'offset_text' => '[GMT-04:00]', 'name' => 'Bolivia Time (America/ La Paz)'),
- 'America/Santiago' => array('offset' => '-4', 'offset_text' => '[GMT-04:00]', 'name' => 'Chile Time (America/ Santiago)'),
- 'America/Guyana' => array('offset' => '-4', 'offset_text' => '[GMT-04:00]', 'name' => 'Guyana Time (America/ Guyana)'),
- 'America/Asuncion' => array('offset' => '-4', 'offset_text' => '[GMT-04:00]', 'name' => 'Paraguay Time (America/ Asuncion)'),
- 'America/Caracas' => array('offset' => '-4', 'offset_text' => '[GMT-04:00]', 'name' => 'Venezuela Time (America/ Caracas)'),
- 'America/Porto_Acre' => array('offset' => '-5', 'offset_text' => '[GMT-05:00]', 'name' => 'Acre Time (America/ Porto Acre)'),
- 'America/Havana' => array('offset' => '-5', 'offset_text' => '[GMT-05:00]', 'name' => 'Central Standard Time (America/ Havana)'),
- 'America/Bogota' => array('offset' => '-5', 'offset_text' => '[GMT-05:00]', 'name' => 'Colombia Time (America/ Bogota)'),
- 'America/Jamaica' => array('offset' => '-5', 'offset_text' => '[GMT-05:00]', 'name' => 'Eastern Standard Time (America/ Jamaica)'),
- 'America/Indianapolis' => array('offset' => '-5', 'offset_text' => '[GMT-05:00]', 'name' => 'Eastern Standard Time (US/ East-Indiana)'),
- 'America/Guayaquil' => array('offset' => '-5', 'offset_text' => '[GMT-05:00]', 'name' => 'Ecuador Time (America/ Guayaquil)'),
- 'America/Lima' => array('offset' => '-6', 'offset_text' => '[GMT-05:00]', 'name' => 'Peru Time (America/ Lima)'),
- 'America/El_Salvador' => array('offset' => '-6', 'offset_text' => '[GMT-06:00]', 'name' => 'Central Standard Time (America/ El Salvador)'),
- 'America/Regina' => array('offset' => '-6', 'offset_text' => '[GMT-06:00]', 'name' => 'Central Standard Time (Canada/ Saskatchewan)'),
- 'America/Chicago' => array('offset' => '-6', 'offset_text' => '[GMT-06:00]', 'name' => 'Central Standard Time (US & Canada)'),
- 'America/Phoenix' => array('offset' => '-7', 'offset_text' => '[GMT-07:00]', 'name' => 'Mountain Standard Time (US/ Arizona)'),
- 'America/Los_Angeles' => array('offset' => '-8', 'offset_text' => '[GMT-08:00]', 'name' => 'Pacific Standard Time (US & Canada)'),
- 'America/Anchorage' => array('offset' => '-9', 'offset_text' => '[GMT-09:00]', 'name' => 'Alaska Standard Time (AST)'),
- 'America/Adak' => array('offset' => '-10', 'offset_text' => '[GMT-10:00]', 'name' => 'Hawaii-Aleutian Standard Time (America/ Adak)'),
- ),
- 'Antarctica' => array(
- 'Antarctica/Syowa' => array('offset' => '3', 'offset_text' => '[GMT+03:00]', 'name' => 'Syowa Time (Antarctica/ Syowa)'),
- 'Antarctica/Mawson' => array('offset' => '6', 'offset_text' => '[GMT+06:00]', 'name' => 'Mawson Time (Antarctica/ Mawson)'),
- 'Antarctica/Vostok' => array('offset' => '6', 'offset_text' => '[GMT+06:00]', 'name' => 'Vostok Time (Antarctica/ Vostok)'),
- 'Antarctica/Davis' => array('offset' => '7', 'offset_text' => '[GMT+07:00]', 'name' => 'Davis Time (Antarctica/ Davis)'),
- 'Antarctica/DumontDUrville' => array('offset' => '10', 'offset_text' => '[GMT+10:00]', 'name' => 'Dumont-d\'Urville Time (Antarctica/ DumontDUrville)'),
- 'Antarctica/Rothera' => array('offset' => '-3', 'offset_text' => '[GMT-03:00]', 'name' => 'Rothera Time (Antarctica/ Rothera)'),
- ),
- 'Asia' => array(
- 'Asia/Jerusalem' => array('offset' => '2', 'offset_text' => '[GMT+02:00]', 'name' => 'Israel Standard Time (Asia/ Jerusalem)'),
- 'Asia/Baghdad' => array('offset' => '3', 'offset_text' => '[GMT+03:00]', 'name' => 'Arabia Standard Time (Asia/ Baghdad)'),
- 'Asia/Kuwait' => array('offset' => '3', 'offset_text' => '[GMT+03:00]', 'name' => 'Arabia Standard Time (Asia/ Kuwait)'),
- 'Asia/Tehran' => array('offset' => '3.5', 'offset_text' => '[GMT+03:30]', 'name' => 'Iran Standard Time (Asia/ Tehran)'),
- 'Asia/Aqtau' => array('offset' => '4', 'offset_text' => '[GMT+04:00]', 'name' => 'Aqtau Time (Asia/ Aqtau)'),
- 'Asia/Yerevan' => array('offset' => '4', 'offset_text' => '[GMT+04:00]', 'name' => 'Armenia Time (NET)'),
- 'Asia/Baku' => array('offset' => '4', 'offset_text' => '[GMT+04:00]', 'name' => 'Azerbaijan Time (Asia/ Baku)'),
- 'Asia/Tbilisi' => array('offset' => '4', 'offset_text' => '[GMT+04:00]', 'name' => 'Georgia Time (Asia/ Tbilisi)'),
- 'Asia/Dubai' => array('offset' => '4', 'offset_text' => '[GMT+04:00]', 'name' => 'Gulf Standard Time (Asia/ Dubai)'),
- 'Asia/Oral' => array('offset' => '4', 'offset_text' => '[GMT+04:00]', 'name' => 'Oral Time (Asia/ Oral)'),
- 'Asia/Kabul' => array('offset' => '4.5', 'offset_text' => '[GMT+04:30]', 'name' => 'Afghanistan Time (Asia/ Kabul)'),
- 'Asia/Aqtobe' => array('offset' => '5', 'offset_text' => '[GMT+05:00]', 'name' => 'Aqtobe Time (Asia/ Aqtobe)'),
- 'Asia/Bishkek' => array('offset' => '5', 'offset_text' => '[GMT+05:00]', 'name' => 'Kirgizstan Time (Asia/ Bishkek)'),
- 'Asia/Karachi' => array('offset' => '5', 'offset_text' => '[GMT+05:00]', 'name' => 'Pakistan Time (PLT)'),
- 'Asia/Dushanbe' => array('offset' => '5', 'offset_text' => '[GMT+05:00]', 'name' => 'Tajikistan Time (Asia/ Dushanbe)'),
- 'Asia/Ashgabat' => array('offset' => '5', 'offset_text' => '[GMT+05:00]', 'name' => 'Turkmenistan Time (Asia/ Ashgabat)'),
- 'Asia/Tashkent' => array('offset' => '5', 'offset_text' => '[GMT+05:00]', 'name' => 'Uzbekistan Time (Asia/ Tashkent)'),
- 'Asia/Yekaterinburg' => array('offset' => '5', 'offset_text' => '[GMT+05:00]', 'name' => 'Yekaterinburg Time (Asia/ Yekaterinburg)'),
- 'Asia/Katmandu' => array('offset' => '5.75', 'offset_text' => '[GMT+05:45]', 'name' => 'Nepal Time (Asia/ Katmandu)'),
- 'Asia/Almaty' => array('offset' => '6', 'offset_text' => '[GMT+06:00]', 'name' => 'Alma-Ata Time (Asia/ Almaty)'),
- 'Asia/Thimbu' => array('offset' => '6', 'offset_text' => '[GMT+06:00]', 'name' => 'Bhutan Time (Asia/ Thimbu)'),
- 'Asia/Novosibirsk' => array('offset' => '6', 'offset_text' => '[GMT+06:00]', 'name' => 'Novosibirsk Time (Asia/ Novosibirsk)'),
- 'Asia/Omsk' => array('offset' => '6', 'offset_text' => '[GMT+06:00]', 'name' => 'Omsk Time (Asia/ Omsk)'),
- 'Asia/Qyzylorda' => array('offset' => '6', 'offset_text' => '[GMT+06:00]', 'name' => 'Qyzylorda Time (Asia/ Qyzylorda)'),
- 'Asia/Colombo' => array('offset' => '6', 'offset_text' => '[GMT+06:00]', 'name' => 'Sri Lanka Time (Asia/ Colombo)'),
- 'Asia/Rangoon' => array('offset' => '6.5', 'offset_text' => '[GMT+06:30]', 'name' => 'Myanmar Time (Asia/ Rangoon)'),
- 'Asia/Hovd' => array('offset' => '7', 'offset_text' => '[GMT+07:00]', 'name' => 'Hovd Time (Asia/ Hovd)'),
- 'Asia/Krasnoyarsk' => array('offset' => '7', 'offset_text' => '[GMT+07:00]', 'name' => 'Krasnoyarsk Time (Asia/ Krasnoyarsk)'),
- 'Asia/Jakarta' => array('offset' => '7', 'offset_text' => '[GMT+07:00]', 'name' => 'West Indonesia Time (Asia/ Jakarta)'),
- 'Asia/Brunei' => array('offset' => '8', 'offset_text' => '[GMT+08:00]', 'name' => 'Brunei Time (Asia/ Brunei)'),
- 'Asia/Makassar' => array('offset' => '8', 'offset_text' => '[GMT+08:00]', 'name' => 'Central Indonesia Time (Asia/ Makassar)'),
- 'Asia/Hong_Kong' => array('offset' => '8', 'offset_text' => '[GMT+08:00]', 'name' => 'Hong Kong Time (Asia/ Hong Kong)'),
- 'Asia/Irkutsk' => array('offset' => '8', 'offset_text' => '[GMT+08:00]', 'name' => 'Irkutsk Time (Asia/ Irkutsk)'),
- 'Asia/Kuala_Lumpur' => array('offset' => '8', 'offset_text' => '[GMT+08:00]', 'name' => 'Malaysia Time (Asia/ Kuala Lumpur)'),
- 'Asia/Manila' => array('offset' => '8', 'offset_text' => '[GMT+08:00]', 'name' => 'Philippines Time (Asia/ Manila)'),
- 'Asia/Shanghai' => array('offset' => '8', 'offset_text' => '[GMT+08:00]', 'name' => 'Shanghai Time (Asia/ Shanghai)'),
- 'Asia/Singapore' => array('offset' => '8', 'offset_text' => '[GMT+08:00]', 'name' => 'Singapore Time (Asia/ Singapore)'),
- 'Asia/Taipei' => array('offset' => '8', 'offset_text' => '[GMT+08:00]', 'name' => 'Taipei Time (Asia/ Taipei)'),
- 'Asia/Ulaanbaatar' => array('offset' => '8', 'offset_text' => '[GMT+08:00]', 'name' => 'Ulaanbaatar Time (Asia/ Ulaanbaatar)'),
- 'Asia/Choibalsan' => array('offset' => '9', 'offset_text' => '[GMT+09:00]', 'name' => 'Choibalsan Time (Asia/ Choibalsan)'),
- 'Asia/Jayapura' => array('offset' => '9', 'offset_text' => '[GMT+09:00]', 'name' => 'East Indonesia Time (Asia/ Jayapura)'),
- 'Asia/Dili' => array('offset' => '9', 'offset_text' => '[GMT+09:00]', 'name' => 'East Timor Time (Asia/ Dili)'),
- 'Asia/Tokyo' => array('offset' => '9', 'offset_text' => '[GMT+09:00]', 'name' => 'Japan Standard Time (JST)'),
- 'Asia/Seoul' => array('offset' => '9', 'offset_text' => '[GMT+09:00]', 'name' => 'Korea Standard Time (Asia/ Seoul)'),
- 'Asia/Yakutsk' => array('offset' => '9', 'offset_text' => '[GMT+09:00]', 'name' => 'Yakutsk Time (Asia/ Yakutsk)'),
- 'Asia/Sakhalin' => array('offset' => '10', 'offset_text' => '[GMT+10:00]', 'name' => 'Sakhalin Time (Asia/ Sakhalin)'),
- 'Asia/Vladivostok' => array('offset' => '10', 'offset_text' => '[GMT+10:00]', 'name' => 'Vladivostok Time (Asia/ Vladivostok)'),
- 'Asia/Magadan' => array('offset' => '11', 'offset_text' => '[GMT+11:00]', 'name' => 'Magadan Time (Asia/ Magadan)'),
- 'Asia/Anadyr' => array('offset' => '12', 'offset_text' => '[GMT+12:00]', 'name' => 'Anadyr Time (Asia/ Anadyr)'),
- 'Asia/Kamchatka' => array('offset' => '12', 'offset_text' => '[GMT+12:00]', 'name' => 'Petropavlovsk-Kamchatski Time (Asia/ Kamchatka)'),
- ),
- 'Atlantic Ocean' => array(
- 'Atlantic/Jan_Mayen' => array('offset' => '1', 'offset_text' => '[GMT+01:00]', 'name' => 'Eastern Greenland Time (Atlantic/ Jan Mayen)'),
- 'Atlantic/Azores' => array('offset' => '-1', 'offset_text' => '[GMT-01:00]', 'name' => 'Azores Time (Atlantic/ Azores)'),
- 'Atlantic/Cape_Verde' => array('offset' => '-1', 'offset_text' => '[GMT-01:00]', 'name' => 'Cape Verde Time (Atlantic/ Cape Verde)'),
- 'Atlantic/South_Georgia' => array('offset' => '-2', 'offset_text' => '[GMT-02:00]', 'name' => 'South Georgia Standard Time (Atlantic/ South Georgia)'),
- 'Atlantic/Bermuda' => array('offset' => '-4', 'offset_text' => '[GMT-04:00]', 'name' => 'Atlantic Standard Time (Atlantic/ Bermuda)'),
- 'Atlantic/Stanley' => array('offset' => '-4', 'offset_text' => '[GMT-04:00]', 'name' => 'Falkland Is. Time (Atlantic/ Stanley)'),
- ),
- 'Australia' => array(
- 'Australia/Perth' => array('offset' => '8', 'offset_text' => '[GMT+08:00]', 'name' => 'Western Standard Time (Australia) (Australia/ Perth)'),
- 'Australia/Broken_Hill' => array('offset' => '9.5', 'offset_text' => '[GMT+09:30]', 'name' => 'Central Standard Time (Australia/ Broken Hill)'),
- 'Australia/Darwin' => array('offset' => '9.5', 'offset_text' => '[GMT+09:30]', 'name' => 'Central Standard Time (Northern Territory) (ACT)'),
- 'Australia/Adelaide' => array('offset' => '9.5', 'offset_text' => '[GMT+09:30]', 'name' => 'Central Standard Time (South Australia) (Australia/ Adelaide)'),
- 'Australia/Sydney' => array('offset' => '10', 'offset_text' => '[GMT+10:00]', 'name' => 'Eastern Standard Time (New South Wales) (Australia/ Sydney)'),
- 'Australia/Brisbane' => array('offset' => '10', 'offset_text' => '[GMT+10:00]', 'name' => 'Eastern Standard Time (Queensland) (Australia/ Brisbane)'),
- 'Australia/Hobart' => array('offset' => '10', 'offset_text' => '[GMT+10:00]', 'name' => 'Eastern Standard Time (Tasmania) (Australia/ Hobart)'),
- 'Australia/Melbourne' => array('offset' => '10', 'offset_text' => '[GMT+10:00]', 'name' => 'Eastern Standard Time (Victoria) (Australia/ Melbourne)'),
- 'Australia/Lord_Howe' => array('offset' => '10.5', 'offset_text' => '[GMT+10:30]', 'name' => 'Load Howe Standard Time (Australia/ Lord Howe)'),
- ),
- 'Europe' => array(
- 'Europe/Lisbon' => array('offset' => '0', 'offset_text' => '[GMT+00:00]', 'name' => 'Western European Time (Europe/ Lisbon)'),
- 'Europe/Berlin' => array('offset' => '1', 'offset_text' => '[GMT+01:00]', 'name' => 'Central European Time (Europe/ Berlin)'),
- 'Europe/Istanbul' => array('offset' => '2', 'offset_text' => '[GMT+02:00]', 'name' => 'Eastern European Time (Europe/ Istanbul)'),
- 'Europe/Moscow' => array('offset' => '3', 'offset_text' => '[GMT+03:00]', 'name' => 'Moscow Standard Time (Europe/ Moscow)'),
- 'Europe/Samara' => array('offset' => '4', 'offset_text' => '[GMT+04:00]', 'name' => 'Samara Time (Europe/ Samara)'),
- ),
- 'Indian' => array(
- 'Indian/Antananarivo' => array('offset' => '3', 'offset_text' => '[GMT+03:00]', 'name' => 'Antananarivo Time (Indian/ Antananarivo)'),
- 'Indian/Comoro' => array('offset' => '3', 'offset_text' => '[GMT+03:00]', 'name' => 'Comoro Time (Indian/ Comoro)'),
- 'Indian/Mayotte' => array('offset' => '3', 'offset_text' => '[GMT+03:00]', 'name' => 'Mayotte Time (Indian/ Mayotte)'),
- 'Indian/Mauritius' => array('offset' => '4', 'offset_text' => '[GMT+04:00]', 'name' => 'Mauritius Time (Indian/ Mauritius)'),
- 'Indian/Reunion' => array('offset' => '4', 'offset_text' => '[GMT+04:00]', 'name' => 'Reunion Time (Indian/ Reunion)'),
- 'Indian/Mahe' => array('offset' => '4', 'offset_text' => '[GMT+04:00]', 'name' => 'Seychelles Time (Indian/ Mahe)'),
- 'Indian/Kerguelen' => array('offset' => '5', 'offset_text' => '[GMT+05:00]', 'name' => 'French Southern & Antarctic Lands Time (Indian/ Kerguelen)'),
- 'Indian/Maldives' => array('offset' => '5', 'offset_text' => '[GMT+05:00]', 'name' => 'Maldives Time (Indian/ Maldives)'),
- 'Indian/IST' => array('offset' => '5.5', 'offset_text' => '[GMT+05:30]', 'name' => 'India Standard Time (India Time / IST)'),
- 'Indian/Chagos' => array('offset' => '6', 'offset_text' => '[GMT+06:00]', 'name' => 'Indian Ocean Territory Time (Indian/ Chagos)'),
- 'Indian/Cocos' => array('offset' => '6.5', 'offset_text' => '[GMT+06:30]', 'name' => 'Cocos Islands Time (Indian/ Cocos)'),
- 'Indian/Christmas' => array('offset' => '7', 'offset_text' => '[GMT+07:00]', 'name' => 'Christmas Island Time (Indian/ Christmas)'),
- ),
- 'Pacific Ocean' => array(
- 'Pacific/Palau' => array('offset' => '9', 'offset_text' => '[GMT+09:00]', 'name' => 'Palau Time (Pacific/ Palau)'),
- 'Pacific/Guam' => array('offset' => '10', 'offset_text' => '[GMT+10:00]', 'name' => 'Chamorro Standard Time (Pacific/ Guam)'),
- 'Pacific/Port_Moresby' => array('offset' => '10', 'offset_text' => '[GMT+10:00]', 'name' => 'Papua New Guinea Time (Pacific/ Port Moresby)'),
- 'Pacific/Truk' => array('offset' => '10', 'offset_text' => '[GMT+10:00]', 'name' => 'Truk Time (Pacific/ Truk)'),
- 'Pacific/Yap' => array('offset' => '10', 'offset_text' => '[GMT+10:00]', 'name' => 'Yap Time (Pacific/ Yap)'),
- 'Pacific/Kosrae' => array('offset' => '11', 'offset_text' => '[GMT+11:00]', 'name' => 'Kosrae Time (Pacific/ Kosrae)'),
- 'Pacific/Noumea' => array('offset' => '11', 'offset_text' => '[GMT+11:00]', 'name' => 'New Caledonia Time (Pacific/ Noumea)'),
- 'Pacific/Ponape' => array('offset' => '11', 'offset_text' => '[GMT+11:00]', 'name' => 'Ponape Time (Pacific/ Ponape)'),
- 'Pacific/Efate' => array('offset' => '11', 'offset_text' => '[GMT+11:00]', 'name' => 'Vanuatu Time (Pacific/ Efate)'),
- 'Pacific/Norfolk' => array('offset' => '11.5', 'offset_text' => '[GMT+11:30]', 'name' => 'Norfolk Time (Pacific/ Norfolk)'),
- 'Pacific/Fiji' => array('offset' => '12', 'offset_text' => '[GMT+12:00]', 'name' => 'Fiji Time (Pacific/ Fiji)'),
- 'Pacific/Tarawa' => array('offset' => '12', 'offset_text' => '[GMT+12:00]', 'name' => 'Gilbert Is. Time (Pacific/ Tarawa)'),
- 'Pacific/Majuro' => array('offset' => '12', 'offset_text' => '[GMT+12:00]', 'name' => 'Marshall Islands Time (Pacific/ Majuro)'),
- 'Pacific/Nauru' => array('offset' => '12', 'offset_text' => '[GMT+12:00]', 'name' => 'Nauru Time (Pacific/ Nauru)'),
- 'Pacific/Auckland' => array('offset' => '12', 'offset_text' => '[GMT+12:00]', 'name' => 'New Zealand Standard Time (Pacific/ Auckland)'),
- 'Pacific/Funafuti' => array('offset' => '12', 'offset_text' => '[GMT+12:00]', 'name' => 'Tuvalu Time (Pacific/ Funafuti)'),
- 'Pacific/Wake' => array('offset' => '12', 'offset_text' => '[GMT+12:00]', 'name' => 'Wake Time (Pacific/ Wake)'),
- 'Pacific/Wallis' => array('offset' => '12', 'offset_text' => '[GMT+12:00]', 'name' => 'Wallis & Futuna Time (Pacific/ Wallis)'),
- 'Pacific/Chatham' => array('offset' => '12.75', 'offset_text' => '[GMT+12:45]', 'name' => 'Chatham Standard Time (Pacific/ Chatham)'),
- 'Pacific/Enderbury' => array('offset' => '13', 'offset_text' => '[GMT+13:00]', 'name' => 'Phoenix Is. Time (Pacific/ Enderbury)'),
- 'Pacific/Tongatapu' => array('offset' => '13', 'offset_text' => '[GMT+13:00]', 'name' => 'Tonga Time (Pacific/ Tongatapu)'),
- 'Pacific/Kiritimati' => array('offset' => '14', 'offset_text' => '[GMT+14:00]', 'name' => 'Line Is. Time (Pacific/ Kiritimati)'),
- 'Pacific/Easter' => array('offset' => '-6', 'offset_text' => '[GMT-06:00]', 'name' => 'Easter Is. Time (Pacific/ Easter)'),
- 'Pacific/Galapagos' => array('offset' => '-6', 'offset_text' => '[GMT-06:00]', 'name' => 'Galapagos Time (Pacific/ Galapagos)'),
- 'Pacific/Pitcairn' => array('offset' => '-8', 'offset_text' => '[GMT-08:00]', 'name' => 'Pitcairn Standard Time (Pacific/ Pitcairn)'),
- 'Pacific/Gambier' => array('offset' => '-9', 'offset_text' => '[GMT-09:00]', 'name' => 'Gambier Time (Pacific/ Gambier)'),
- 'Pacific/Marquesas' => array('offset' => '-9.5', 'offset_text' => '[GMT-09:30]', 'name' => 'Marquesas Time (Pacific/ Marquesas)'),
- 'Pacific/Rarotonga' => array('offset' => '-10', 'offset_text' => '[GMT-10:00]', 'name' => 'Cook Is. Time (Pacific/ Rarotonga)'),
- 'Pacific/Tahiti' => array('offset' => '-10', 'offset_text' => '[GMT-10:00]', 'name' => 'Tahiti Time (Pacific/ Tahiti)'),
- 'Pacific/Fakaofo' => array('offset' => '-10', 'offset_text' => '[GMT-10:00]', 'name' => 'Tokelau Time (Pacific/ Fakaofo)'),
- 'Pacific/Niue' => array('offset' => '-11', 'offset_text' => '[GMT-11:00]', 'name' => 'Niue Time (Pacific/ Niue)'),
- 'Pacific/Apia' => array('offset' => '-11', 'offset_text' => '[GMT-11:00]', 'name' => 'West Samoa Time (MIT)'),
- ),
- );
- }
-
+
+ /**
+ * Class default constructor
+ */
+ function __construct()
+ {
+ }
+
+ /**
+ * Returns the instance of object
+ *
+ * @return current class
+ */
+ public static function init()
+ {
+ return parent::init(__CLASS__);
+ }
+
+ /**
+ * Sets the time zone used by the application
+ *
+ * @param string $value
+ *
+ * @see http://php.net/manual/en/function.date-default-timezone-set.php
+ */
+ public function setTimeZone($value)
+ {
+ date_default_timezone_set($value);
+ }
+
+ /**
+ * Returns the time zone used by the application
+ *
+ * @return string
+ * @see http://php.net/manual/en/function.date-default-timezone-set.php
+ */
+ public function getTimeZone()
+ {
+ return date_default_timezone_get();
+ }
+
+ /**
+ * Returns a list of locales
+ *
+ * @return array
+ */
+ public function getLocales()
+ {
+ return [
+ 'sq_AL' => A::t('i18n', 'languages.sq').' - '.A::t('i18n', 'countries.al'),
+ 'ar_AE' => A::t('i18n', 'languages.ar').' - '.A::t('i18n', 'countries.ae'),
+ 'ar_BH' => A::t('i18n', 'languages.ar').' - '.A::t('i18n', 'countries.bh'),
+ 'ar_DZ' => A::t('i18n', 'languages.ar').' - '.A::t('i18n', 'countries.dz'),
+ 'ar_EG' => A::t('i18n', 'languages.ar').' - '.A::t('i18n', 'countries.eg'),
+ 'ar_IN' => A::t('i18n', 'languages.ar').' - '.A::t('i18n', 'countries.in'),
+ 'ar_IQ' => A::t('i18n', 'languages.ar').' - '.A::t('i18n', 'countries.iq'),
+ 'ar_JO' => A::t('i18n', 'languages.ar').' - '.A::t('i18n', 'countries.jo'),
+ 'ar_KW' => A::t('i18n', 'languages.ar').' - '.A::t('i18n', 'countries.kw'),
+ 'ar_LB' => A::t('i18n', 'languages.ar').' - '.A::t('i18n', 'countries.lb'),
+ 'ar_LY' => A::t('i18n', 'languages.ar').' - '.A::t('i18n', 'countries.ly'),
+ 'ar_MA' => A::t('i18n', 'languages.ar').' - '.A::t('i18n', 'countries.ma'),
+ 'ar_OM' => A::t('i18n', 'languages.ar').' - '.A::t('i18n', 'countries.om'),
+ 'ar_QA' => A::t('i18n', 'languages.ar').' - '.A::t('i18n', 'countries.qa'),
+ 'ar_SA' => A::t('i18n', 'languages.ar').' - '.A::t('i18n', 'countries.sa'),
+ 'ar_SD' => A::t('i18n', 'languages.ar').' - '.A::t('i18n', 'countries.sd'),
+ 'ar_SY' => A::t('i18n', 'languages.ar').' - '.A::t('i18n', 'countries.sy'),
+ 'ar_TN' => A::t('i18n', 'languages.ar').' - '.A::t('i18n', 'countries.tn'),
+ 'ar_YE' => A::t('i18n', 'languages.ar').' - '.A::t('i18n', 'countries.ye'),
+ 'eu_ES' => A::t('i18n', 'languages.eu').' - '.A::t('i18n', 'countries.es'),
+ 'be_BY' => A::t('i18n', 'languages.be').' - '.A::t('i18n', 'countries.by'),
+ 'bg_BG' => A::t('i18n', 'languages.bg').' - '.A::t('i18n', 'countries.bg'),
+ 'ca_ES' => A::t('i18n', 'languages.ca').' - '.A::t('i18n', 'countries.es'),
+ 'zh_CN' => A::t('i18n', 'languages.zh').' - '.A::t('i18n', 'countries.cn'),
+ 'zh_HK' => A::t('i18n', 'languages.zh').' - '.A::t('i18n', 'countries.hk'),
+ 'zh_TW' => A::t('i18n', 'languages.zh').' - '.A::t('i18n', 'countries.tw'),
+ 'hr_HR' => A::t('i18n', 'languages.hr').' - '.A::t('i18n', 'countries.hr'),
+ 'cs_CZ' => A::t('i18n', 'languages.cs').' - '.A::t('i18n', 'countries.cz'),
+ 'da_DK' => A::t('i18n', 'languages.da').' - '.A::t('i18n', 'countries.dk'),
+ 'nl_BE' => A::t('i18n', 'languages.nl').' - '.A::t('i18n', 'countries.be'),
+ 'nl_NL' => A::t('i18n', 'languages.nl').' - '.A::t('i18n', 'countries.nl'),
+ 'de_AT' => A::t('i18n', 'languages.de').' - '.A::t('i18n', 'countries.at'),
+ 'de_BE' => A::t('i18n', 'languages.de').' - '.A::t('i18n', 'countries.be'),
+ 'de_CH' => A::t('i18n', 'languages.de').' - '.A::t('i18n', 'countries.ch'),
+ 'de_DE' => A::t('i18n', 'languages.de').' - '.A::t('i18n', 'countries.de'),
+ 'de_LU' => A::t('i18n', 'languages.de').' - '.A::t('i18n', 'countries.lu'),
+ 'en_AU' => A::t('i18n', 'languages.en').' - '.A::t('i18n', 'countries.au'),
+ 'en_CA' => A::t('i18n', 'languages.en').' - '.A::t('i18n', 'countries.ca'),
+ 'en_GB' => A::t('i18n', 'languages.en').' - '.A::t('i18n', 'countries.gb'),
+ 'en_IN' => A::t('i18n', 'languages.en').' - '.A::t('i18n', 'countries.in'),
+ 'en_NZ' => A::t('i18n', 'languages.en').' - '.A::t('i18n', 'countries.nz'),
+ 'en_PH' => A::t('i18n', 'languages.en').' - '.A::t('i18n', 'countries.ph'),
+ 'en_US' => A::t('i18n', 'languages.en').' - '.A::t('i18n', 'countries.us'),
+ 'en_ZA' => A::t('i18n', 'languages.en').' - '.A::t('i18n', 'countries.za'),
+ 'en_ZW' => A::t('i18n', 'languages.en').' - '.A::t('i18n', 'countries.zw'),
+ 'et_EE' => A::t('i18n', 'languages.et').' - '.A::t('i18n', 'countries.ee'),
+ 'fi_FI' => A::t('i18n', 'languages.fi').' - '.A::t('i18n', 'countries.fi'),
+ 'fo_FO' => A::t('i18n', 'languages.fo').' - '.A::t('i18n', 'countries.fo'),
+ 'fr_BE' => A::t('i18n', 'languages.fr').' - '.A::t('i18n', 'countries.be'),
+ 'fr_CA' => A::t('i18n', 'languages.fr').' - '.A::t('i18n', 'countries.ca'),
+ 'fr_CH' => A::t('i18n', 'languages.fr').' - '.A::t('i18n', 'countries.ch'),
+ 'fr_FR' => A::t('i18n', 'languages.fr').' - '.A::t('i18n', 'countries.fr'),
+ 'fr_LU' => A::t('i18n', 'languages.fr').' - '.A::t('i18n', 'countries.lu'),
+ 'gl_ES' => A::t('i18n', 'languages.gl').' - '.A::t('i18n', 'countries.es'),
+ 'el_GR' => A::t('i18n', 'languages.el').' - '.A::t('i18n', 'countries.gr'),
+ 'gu_IN' => A::t('i18n', 'languages.gu').' - '.A::t('i18n', 'countries.in'),
+ 'he_IL' => A::t('i18n', 'languages.he').' - '.A::t('i18n', 'countries.il'),
+ 'hi_IN' => A::t('i18n', 'languages.hi').' - '.A::t('i18n', 'countries.in'),
+ 'hu_HU' => A::t('i18n', 'languages.hu').' - '.A::t('i18n', 'countries.hu'),
+ 'id_ID' => A::t('i18n', 'languages.id').' - '.A::t('i18n', 'countries.id'),
+ 'is_IS' => A::t('i18n', 'languages.is').' - '.A::t('i18n', 'countries.is'),
+ 'it_CH' => A::t('i18n', 'languages.it').' - '.A::t('i18n', 'countries.ch'),
+ 'it_IT' => A::t('i18n', 'languages.it').' - '.A::t('i18n', 'countries.it'),
+ 'ja_JP' => A::t('i18n', 'languages.ja').' - '.A::t('i18n', 'countries.jp'),
+ 'ko_KR' => A::t('i18n', 'languages.ko').' - '.A::t('i18n', 'countries.kr'),
+ 'lt_LT' => A::t('i18n', 'languages.lt').' - '.A::t('i18n', 'countries.lt'),
+ 'lv_LV' => A::t('i18n', 'languages.lv').' - '.A::t('i18n', 'countries.lv'),
+ 'mk_MK' => A::t('i18n', 'languages.mk').' - '.A::t('i18n', 'countries.mk'),
+ 'mn_MN' => A::t('i18n', 'languages.mn').' - '.A::t('i18n', 'countries.mn'),
+ 'ms_MY' => A::t('i18n', 'languages.ms').' - '.A::t('i18n', 'countries.my'),
+ 'nb_NO' => A::t('i18n', 'languages.nb').' - '.A::t('i18n', 'countries.no'),
+ 'no_NO' => A::t('i18n', 'languages.no').' - '.A::t('i18n', 'countries.no'),
+ 'pl_PL' => A::t('i18n', 'languages.pl').' - '.A::t('i18n', 'countries.pl'),
+ 'pt_BR' => A::t('i18n', 'languages.pt').' - '.A::t('i18n', 'countries.br'),
+ 'pt_PT' => A::t('i18n', 'languages.pt').' - '.A::t('i18n', 'countries.pt'),
+ 'ro_RO' => A::t('i18n', 'languages.ro').' - '.A::t('i18n', 'countries.ro'),
+ 'ru_RU' => A::t('i18n', 'languages.ru').' - '.A::t('i18n', 'countries.ru'),
+ 'ru_UA' => A::t('i18n', 'languages.ru').' - '.A::t('i18n', 'countries.ua'),
+ 'sk_SK' => A::t('i18n', 'languages.sk').' - '.A::t('i18n', 'countries.sk'),
+ 'sl_SI' => A::t('i18n', 'languages.sl').' - '.A::t('i18n', 'countries.si'),
+ 'sr_YU' => A::t('i18n', 'languages.sr').' - '.A::t('i18n', 'countries.rs'),
+ 'es_AR' => A::t('i18n', 'languages.es').' - '.A::t('i18n', 'countries.ar'),
+ 'es_BO' => A::t('i18n', 'languages.es').' - '.A::t('i18n', 'countries.bo'),
+ 'es_CL' => A::t('i18n', 'languages.es').' - '.A::t('i18n', 'countries.cl'),
+ 'es_CO' => A::t('i18n', 'languages.es').' - '.A::t('i18n', 'countries.co'),
+ 'es_CR' => A::t('i18n', 'languages.es').' - '.A::t('i18n', 'countries.cr'),
+ 'es_DO' => A::t('i18n', 'languages.es').' - '.A::t('i18n', 'countries.do'),
+ 'es_EC' => A::t('i18n', 'languages.es').' - '.A::t('i18n', 'countries.ec'),
+ 'es_ES' => A::t('i18n', 'languages.es').' - '.A::t('i18n', 'countries.es'),
+ 'es_GT' => A::t('i18n', 'languages.es').' - '.A::t('i18n', 'countries.gt'),
+ 'es_HN' => A::t('i18n', 'languages.es').' - '.A::t('i18n', 'countries.hn'),
+ 'es_MX' => A::t('i18n', 'languages.es').' - '.A::t('i18n', 'countries.mx'),
+ 'es_NI' => A::t('i18n', 'languages.es').' - '.A::t('i18n', 'countries.ni'),
+ 'es_PA' => A::t('i18n', 'languages.es').' - '.A::t('i18n', 'countries.pa'),
+ 'es_PE' => A::t('i18n', 'languages.es').' - '.A::t('i18n', 'countries.pe'),
+ 'es_PR' => A::t('i18n', 'languages.es').' - '.A::t('i18n', 'countries.pr'),
+ 'es_PY' => A::t('i18n', 'languages.es').' - '.A::t('i18n', 'countries.py'),
+ 'es_SV' => A::t('i18n', 'languages.es').' - '.A::t('i18n', 'countries.sv'),
+ 'es_US' => A::t('i18n', 'languages.es').' - '.A::t('i18n', 'countries.us'),
+ 'es_UY' => A::t('i18n', 'languages.es').' - '.A::t('i18n', 'countries.uy'),
+ 'es_VE' => A::t('i18n', 'languages.es').' - '.A::t('i18n', 'countries.ve'),
+ 'sv_FI' => A::t('i18n', 'languages.sv').' - '.A::t('i18n', 'countries.fi'),
+ 'sv_SE' => A::t('i18n', 'languages.sv').' - '.A::t('i18n', 'countries.se'),
+ 'ta_IN' => A::t('i18n', 'languages.ta').' - '.A::t('i18n', 'countries.in'),
+ 'te_IN' => A::t('i18n', 'languages.te').' - '.A::t('i18n', 'countries.in'),
+ 'th_TH' => A::t('i18n', 'languages.th').' - '.A::t('i18n', 'countries.th'),
+ 'tr_TR' => A::t('i18n', 'languages.tr').' - '.A::t('i18n', 'countries.tr'),
+ 'uk_UA' => A::t('i18n', 'languages.uk').' - '.A::t('i18n', 'countries.ua'),
+ 'ur_PK' => A::t('i18n', 'languages.ur').' - '.A::t('i18n', 'countries.pk'),
+ 'vi_VN' => A::t('i18n', 'languages.vi').' - '.A::t('i18n', 'countries.vn'),
+ ];
+ }
+
+ /**
+ * Returns a timzone offset or full name by time zone name
+ *
+ * @param string $name
+ * @param string $type 'offset' - default, 'offset_name' or 'full_name'
+ *
+ * @return float|string
+ */
+ public function getTimeZoneInfo($name = '', $type = 'offset')
+ {
+ $return = '';
+
+ if ( ! empty($name)) {
+ $timeZones = $this->getTimeZones();
+ foreach ($timeZones as $timeZone) {
+ foreach ($timeZone as $zoneName => $zoneInfo) {
+ if (strtolower($zoneName) == strtolower($name)) {
+ if ($type == 'offset') {
+ $return = isset($zoneInfo['offset']) ? $zoneInfo['offset'] : '';
+ } elseif ($type == 'offset_name') {
+ $return = (isset($zoneInfo['offset_text']) && isset($zoneInfo['name']))
+ ? $zoneInfo['offset_text'] : '';
+ } elseif ($type == 'full_name') {
+ $return = (isset($zoneInfo['offset_text']) && isset($zoneInfo['name']))
+ ? $zoneInfo['offset_text'].' '.$zoneInfo['name'] : '';
+ }
+ break(2);
+ }
+ }
+ }
+ }
+
+ return $return;
+ }
+
+ /**
+ * Returns a nested array of timzones by continents
+ *
+ * @return array
+ */
+ public function getTimeZones()
+ {
+ return [
+ 'Africa' => [
+ 'Africa/Casablanca' => [
+ 'offset' => '0',
+ 'offset_text' => '[GMT+00:00]',
+ 'name' => 'Western European Time (Africa/ Casablanca)'
+ ],
+ 'Africa/Algiers' => [
+ 'offset' => '1',
+ 'offset_text' => '[GMT+01:00]',
+ 'name' => 'Central European Time (Africa/ Algiers)'
+ ],
+ 'Africa/Bangui' => [
+ 'offset' => '1',
+ 'offset_text' => '[GMT+01:00]',
+ 'name' => 'Western African Time (Africa/ Bangui)'
+ ],
+ 'Africa/Windhoek' => [
+ 'offset' => '1',
+ 'offset_text' => '[GMT+01:00]',
+ 'name' => 'Western African Time (Africa/ Windhoek)'
+ ],
+ 'Africa/Tripoli' => [
+ 'offset' => '2',
+ 'offset_text' => '[GMT+02:00]',
+ 'name' => 'Eastern European Time (Africa/ Tripoli)'
+ ],
+ 'Africa/Johannesburg' => [
+ 'offset' => '2',
+ 'offset_text' => '[GMT+02:00]',
+ 'name' => 'South Africa Standard Time (Africa/ Johannesburg)'
+ ],
+ 'Africa/Dar_es_Salaam' => [
+ 'offset' => '3',
+ 'offset_text' => '[GMT+03:00]',
+ 'name' => 'Eastern African Time (EAT)'
+ ],
+ ],
+ 'America (North & South)' => [
+ 'America/Scoresbysund' => [
+ 'offset' => '-1',
+ 'offset_text' => '[GMT-01:00]',
+ 'name' => 'Eastern Greenland Time (America/ Scoresbysund)'
+ ],
+ 'America/Noronha' => [
+ 'offset' => '-2',
+ 'offset_text' => '[GMT-02:00]',
+ 'name' => 'Fernando de Noronha Time (America/ Noronha)'
+ ],
+ 'America/Argentina/Buenos_Aires' => ['offset' => '-3',
+ 'offset_text' => '[GMT-03:00]',
+ 'name' => 'Argentine Time (AGT)'
+ ],
+ 'America/Belem' => ['offset' => '-3',
+ 'offset_text' => '[GMT-03:00]',
+ 'name' => 'Brazil Time (America/ Belem)'
+ ],
+ 'America/Sao_Paulo' => ['offset' => '-3',
+ 'offset_text' => '[GMT-03:00]',
+ 'name' => 'Brazil Time (BET)'
+ ],
+ 'America/Cayenne' => ['offset' => '-3',
+ 'offset_text' => '[GMT-03:00]',
+ 'name' => 'French Guiana Time (America/ Cayenne)'
+ ],
+ 'America/Miquelon' => ['offset' => '-3',
+ 'offset_text' => '[GMT-03:00]',
+ 'name' => 'Pierre & Miquelon Standard Time (America/ Miquelon)'
+ ],
+ 'America/Paramaribo' => ['offset' => '-3',
+ 'offset_text' => '[GMT-03:00]',
+ 'name' => 'Suriname Time (America/ Paramaribo)'
+ ],
+ 'America/Montevideo' => ['offset' => '-3',
+ 'offset_text' => '[GMT-03:00]',
+ 'name' => 'Uruguay Time (America/ Montevideo)'
+ ],
+ 'America/Godthab' => ['offset' => '-3',
+ 'offset_text' => '[GMT-03:00]',
+ 'name' => 'Western Greenland Time (America/ Godthab)'
+ ],
+ 'America/St_Johns' => ['offset' => '-3',
+ 'offset_text' => '[GMT-03:30]',
+ 'name' => 'Newfoundland Standard Time (America/ St Johns)'
+ ],
+ 'America/Cuiaba' => ['offset' => '-4',
+ 'offset_text' => '[GMT-04:00]',
+ 'name' => 'Amazon Standard Time (America/ Cuiaba)'
+ ],
+ 'America/Glace_Bay' => ['offset' => '-4',
+ 'offset_text' => '[GMT-04:00]',
+ 'name' => 'Atlantic Standard Time (America/ Glace Bay)'
+ ],
+ 'America/La_Paz' => ['offset' => '-4',
+ 'offset_text' => '[GMT-04:00]',
+ 'name' => 'Bolivia Time (America/ La Paz)'
+ ],
+ 'America/Santiago' => ['offset' => '-4',
+ 'offset_text' => '[GMT-04:00]',
+ 'name' => 'Chile Time (America/ Santiago)'
+ ],
+ 'America/Guyana' => ['offset' => '-4',
+ 'offset_text' => '[GMT-04:00]',
+ 'name' => 'Guyana Time (America/ Guyana)'
+ ],
+ 'America/Asuncion' => ['offset' => '-4',
+ 'offset_text' => '[GMT-04:00]',
+ 'name' => 'Paraguay Time (America/ Asuncion)'
+ ],
+ 'America/Caracas' => ['offset' => '-4',
+ 'offset_text' => '[GMT-04:00]',
+ 'name' => 'Venezuela Time (America/ Caracas)'
+ ],
+ 'America/Porto_Acre' => ['offset' => '-5',
+ 'offset_text' => '[GMT-05:00]',
+ 'name' => 'Acre Time (America/ Porto Acre)'
+ ],
+ 'America/Havana' => ['offset' => '-5',
+ 'offset_text' => '[GMT-05:00]',
+ 'name' => 'Central Standard Time (America/ Havana)'
+ ],
+ 'America/Bogota' => ['offset' => '-5',
+ 'offset_text' => '[GMT-05:00]',
+ 'name' => 'Colombia Time (America/ Bogota)'
+ ],
+ 'America/Jamaica' => ['offset' => '-5',
+ 'offset_text' => '[GMT-05:00]',
+ 'name' => 'Eastern Standard Time (America/ Jamaica)'
+ ],
+ 'America/Indianapolis' => ['offset' => '-5',
+ 'offset_text' => '[GMT-05:00]',
+ 'name' => 'Eastern Standard Time (US/ East-Indiana)'
+ ],
+ 'America/Guayaquil' => ['offset' => '-5',
+ 'offset_text' => '[GMT-05:00]',
+ 'name' => 'Ecuador Time (America/ Guayaquil)'
+ ],
+ 'America/Lima' => ['offset' => '-6',
+ 'offset_text' => '[GMT-05:00]',
+ 'name' => 'Peru Time (America/ Lima)'
+ ],
+ 'America/El_Salvador' => ['offset' => '-6',
+ 'offset_text' => '[GMT-06:00]',
+ 'name' => 'Central Standard Time (America/ El Salvador)'
+ ],
+ 'America/Regina' => ['offset' => '-6',
+ 'offset_text' => '[GMT-06:00]',
+ 'name' => 'Central Standard Time (Canada/ Saskatchewan)'
+ ],
+ 'America/Chicago' => ['offset' => '-6',
+ 'offset_text' => '[GMT-06:00]',
+ 'name' => 'Central Standard Time (US & Canada)'
+ ],
+ 'America/Phoenix' => ['offset' => '-7',
+ 'offset_text' => '[GMT-07:00]',
+ 'name' => 'Mountain Standard Time (US/ Arizona)'
+ ],
+ 'America/Los_Angeles' => ['offset' => '-8',
+ 'offset_text' => '[GMT-08:00]',
+ 'name' => 'Pacific Standard Time (US & Canada)'
+ ],
+ 'America/Anchorage' => ['offset' => '-9',
+ 'offset_text' => '[GMT-09:00]',
+ 'name' => 'Alaska Standard Time (AST)'
+ ],
+ 'America/Adak' => ['offset' => '-10',
+ 'offset_text' => '[GMT-10:00]',
+ 'name' => 'Hawaii-Aleutian Standard Time (America/ Adak)'
+ ],
+ ],
+ 'Antarctica' => [
+ 'Antarctica/Syowa' => [
+ 'offset' => '3',
+ 'offset_text' => '[GMT+03:00]',
+ 'name' => 'Syowa Time (Antarctica/ Syowa)'
+ ],
+ 'Antarctica/Mawson' => [
+ 'offset' => '6',
+ 'offset_text' => '[GMT+06:00]',
+ 'name' => 'Mawson Time (Antarctica/ Mawson)'
+ ],
+ 'Antarctica/Vostok' => [
+ 'offset' => '6',
+ 'offset_text' => '[GMT+06:00]',
+ 'name' => 'Vostok Time (Antarctica/ Vostok)'
+ ],
+ 'Antarctica/Davis' => [
+ 'offset' => '7',
+ 'offset_text' => '[GMT+07:00]',
+ 'name' => 'Davis Time (Antarctica/ Davis)'
+ ],
+ 'Antarctica/DumontDUrville' => ['offset' => '10',
+ 'offset_text' => '[GMT+10:00]',
+ 'name' => 'Dumont-d\'Urville Time (Antarctica/ DumontDUrville)'
+ ],
+ 'Antarctica/Rothera' => ['offset' => '-3',
+ 'offset_text' => '[GMT-03:00]',
+ 'name' => 'Rothera Time (Antarctica/ Rothera)'
+ ],
+ ],
+ 'Asia' => [
+ 'Asia/Jerusalem' => [
+ 'offset' => '2',
+ 'offset_text' => '[GMT+02:00]',
+ 'name' => 'Israel Standard Time (Asia/ Jerusalem)'
+ ],
+ 'Asia/Baghdad' => [
+ 'offset' => '3',
+ 'offset_text' => '[GMT+03:00]',
+ 'name' => 'Arabia Standard Time (Asia/ Baghdad)'
+ ],
+ 'Asia/Kuwait' => [
+ 'offset' => '3',
+ 'offset_text' => '[GMT+03:00]',
+ 'name' => 'Arabia Standard Time (Asia/ Kuwait)'
+ ],
+ 'Asia/Tehran' => [
+ 'offset' => '3.5',
+ 'offset_text' => '[GMT+03:30]',
+ 'name' => 'Iran Standard Time (Asia/ Tehran)'
+ ],
+ 'Asia/Aqtau' => [
+ 'offset' => '4',
+ 'offset_text' => '[GMT+04:00]',
+ 'name' => 'Aqtau Time (Asia/ Aqtau)'
+ ],
+ 'Asia/Yerevan' => ['offset' => '4',
+ 'offset_text' => '[GMT+04:00]',
+ 'name' => 'Armenia Time (NET)'
+ ],
+ 'Asia/Baku' => [
+ 'offset' => '4',
+ 'offset_text' => '[GMT+04:00]',
+ 'name' => 'Azerbaijan Time (Asia/ Baku)'
+ ],
+ 'Asia/Tbilisi' => [
+ 'offset' => '4',
+ 'offset_text' => '[GMT+04:00]',
+ 'name' => 'Georgia Time (Asia/ Tbilisi)'
+ ],
+ 'Asia/Dubai' => [
+ 'offset' => '4',
+ 'offset_text' => '[GMT+04:00]',
+ 'name' => 'Gulf Standard Time (Asia/ Dubai)'
+ ],
+ 'Asia/Oral' => [
+ 'offset' => '4',
+ 'offset_text' => '[GMT+04:00]',
+ 'name' => 'Oral Time (Asia/ Oral)'
+ ],
+ 'Asia/Kabul' => [
+ 'offset' => '4.5',
+ 'offset_text' => '[GMT+04:30]',
+ 'name' => 'Afghanistan Time (Asia/ Kabul)'
+ ],
+ 'Asia/Aqtobe' => [
+ 'offset' => '5',
+ 'offset_text' => '[GMT+05:00]',
+ 'name' => 'Aqtobe Time (Asia/ Aqtobe)'
+ ],
+ 'Asia/Bishkek' => [
+ 'offset' => '5',
+ 'offset_text' => '[GMT+05:00]',
+ 'name' => 'Kirgizstan Time (Asia/ Bishkek)'
+ ],
+ 'Asia/Karachi' => ['offset' => '5',
+ 'offset_text' => '[GMT+05:00]',
+ 'name' => 'Pakistan Time (PLT)'
+ ],
+ 'Asia/Dushanbe' => [
+ 'offset' => '5',
+ 'offset_text' => '[GMT+05:00]',
+ 'name' => 'Tajikistan Time (Asia/ Dushanbe)'
+ ],
+ 'Asia/Ashgabat' => [
+ 'offset' => '5',
+ 'offset_text' => '[GMT+05:00]',
+ 'name' => 'Turkmenistan Time (Asia/ Ashgabat)'
+ ],
+ 'Asia/Tashkent' => [
+ 'offset' => '5',
+ 'offset_text' => '[GMT+05:00]',
+ 'name' => 'Uzbekistan Time (Asia/ Tashkent)'
+ ],
+ 'Asia/Yekaterinburg' => ['offset' => '5',
+ 'offset_text' => '[GMT+05:00]',
+ 'name' => 'Yekaterinburg Time (Asia/ Yekaterinburg)'
+ ],
+ 'Asia/Katmandu' => ['offset' => '5.75',
+ 'offset_text' => '[GMT+05:45]',
+ 'name' => 'Nepal Time (Asia/ Katmandu)'
+ ],
+ 'Asia/Almaty' => ['offset' => '6',
+ 'offset_text' => '[GMT+06:00]',
+ 'name' => 'Alma-Ata Time (Asia/ Almaty)'
+ ],
+ 'Asia/Thimbu' => ['offset' => '6',
+ 'offset_text' => '[GMT+06:00]',
+ 'name' => 'Bhutan Time (Asia/ Thimbu)'
+ ],
+ 'Asia/Novosibirsk' => ['offset' => '6',
+ 'offset_text' => '[GMT+06:00]',
+ 'name' => 'Novosibirsk Time (Asia/ Novosibirsk)'
+ ],
+ 'Asia/Omsk' => ['offset' => '6',
+ 'offset_text' => '[GMT+06:00]',
+ 'name' => 'Omsk Time (Asia/ Omsk)'
+ ],
+ 'Asia/Qyzylorda' => ['offset' => '6',
+ 'offset_text' => '[GMT+06:00]',
+ 'name' => 'Qyzylorda Time (Asia/ Qyzylorda)'
+ ],
+ 'Asia/Colombo' => ['offset' => '6',
+ 'offset_text' => '[GMT+06:00]',
+ 'name' => 'Sri Lanka Time (Asia/ Colombo)'
+ ],
+ 'Asia/Rangoon' => ['offset' => '6.5',
+ 'offset_text' => '[GMT+06:30]',
+ 'name' => 'Myanmar Time (Asia/ Rangoon)'
+ ],
+ 'Asia/Hovd' => ['offset' => '7',
+ 'offset_text' => '[GMT+07:00]',
+ 'name' => 'Hovd Time (Asia/ Hovd)'
+ ],
+ 'Asia/Krasnoyarsk' => ['offset' => '7',
+ 'offset_text' => '[GMT+07:00]',
+ 'name' => 'Krasnoyarsk Time (Asia/ Krasnoyarsk)'
+ ],
+ 'Asia/Jakarta' => ['offset' => '7',
+ 'offset_text' => '[GMT+07:00]',
+ 'name' => 'West Indonesia Time (Asia/ Jakarta)'
+ ],
+ 'Asia/Brunei' => ['offset' => '8',
+ 'offset_text' => '[GMT+08:00]',
+ 'name' => 'Brunei Time (Asia/ Brunei)'
+ ],
+ 'Asia/Makassar' => ['offset' => '8',
+ 'offset_text' => '[GMT+08:00]',
+ 'name' => 'Central Indonesia Time (Asia/ Makassar)'
+ ],
+ 'Asia/Hong_Kong' => ['offset' => '8',
+ 'offset_text' => '[GMT+08:00]',
+ 'name' => 'Hong Kong Time (Asia/ Hong Kong)'
+ ],
+ 'Asia/Irkutsk' => ['offset' => '8',
+ 'offset_text' => '[GMT+08:00]',
+ 'name' => 'Irkutsk Time (Asia/ Irkutsk)'
+ ],
+ 'Asia/Kuala_Lumpur' => ['offset' => '8',
+ 'offset_text' => '[GMT+08:00]',
+ 'name' => 'Malaysia Time (Asia/ Kuala Lumpur)'
+ ],
+ 'Asia/Manila' => ['offset' => '8',
+ 'offset_text' => '[GMT+08:00]',
+ 'name' => 'Philippines Time (Asia/ Manila)'
+ ],
+ 'Asia/Shanghai' => ['offset' => '8',
+ 'offset_text' => '[GMT+08:00]',
+ 'name' => 'Shanghai Time (Asia/ Shanghai)'
+ ],
+ 'Asia/Singapore' => ['offset' => '8',
+ 'offset_text' => '[GMT+08:00]',
+ 'name' => 'Singapore Time (Asia/ Singapore)'
+ ],
+ 'Asia/Taipei' => ['offset' => '8',
+ 'offset_text' => '[GMT+08:00]',
+ 'name' => 'Taipei Time (Asia/ Taipei)'
+ ],
+ 'Asia/Ulaanbaatar' => ['offset' => '8',
+ 'offset_text' => '[GMT+08:00]',
+ 'name' => 'Ulaanbaatar Time (Asia/ Ulaanbaatar)'
+ ],
+ 'Asia/Choibalsan' => ['offset' => '9',
+ 'offset_text' => '[GMT+09:00]',
+ 'name' => 'Choibalsan Time (Asia/ Choibalsan)'
+ ],
+ 'Asia/Jayapura' => ['offset' => '9',
+ 'offset_text' => '[GMT+09:00]',
+ 'name' => 'East Indonesia Time (Asia/ Jayapura)'
+ ],
+ 'Asia/Dili' => ['offset' => '9',
+ 'offset_text' => '[GMT+09:00]',
+ 'name' => 'East Timor Time (Asia/ Dili)'
+ ],
+ 'Asia/Tokyo' => ['offset' => '9',
+ 'offset_text' => '[GMT+09:00]',
+ 'name' => 'Japan Standard Time (JST)'
+ ],
+ 'Asia/Seoul' => ['offset' => '9',
+ 'offset_text' => '[GMT+09:00]',
+ 'name' => 'Korea Standard Time (Asia/ Seoul)'
+ ],
+ 'Asia/Yakutsk' => ['offset' => '9',
+ 'offset_text' => '[GMT+09:00]',
+ 'name' => 'Yakutsk Time (Asia/ Yakutsk)'
+ ],
+ 'Asia/Sakhalin' => ['offset' => '10',
+ 'offset_text' => '[GMT+10:00]',
+ 'name' => 'Sakhalin Time (Asia/ Sakhalin)'
+ ],
+ 'Asia/Vladivostok' => ['offset' => '10',
+ 'offset_text' => '[GMT+10:00]',
+ 'name' => 'Vladivostok Time (Asia/ Vladivostok)'
+ ],
+ 'Asia/Magadan' => ['offset' => '11',
+ 'offset_text' => '[GMT+11:00]',
+ 'name' => 'Magadan Time (Asia/ Magadan)'
+ ],
+ 'Asia/Anadyr' => ['offset' => '12',
+ 'offset_text' => '[GMT+12:00]',
+ 'name' => 'Anadyr Time (Asia/ Anadyr)'
+ ],
+ 'Asia/Kamchatka' => ['offset' => '12',
+ 'offset_text' => '[GMT+12:00]',
+ 'name' => 'Petropavlovsk-Kamchatski Time (Asia/ Kamchatka)'
+ ],
+ ],
+ 'Atlantic Ocean' => [
+ 'Atlantic/Jan_Mayen' => [
+ 'offset' => '1',
+ 'offset_text' => '[GMT+01:00]',
+ 'name' => 'Eastern Greenland Time (Atlantic/ Jan Mayen)'
+ ],
+ 'Atlantic/Azores' => [
+ 'offset' => '-1',
+ 'offset_text' => '[GMT-01:00]',
+ 'name' => 'Azores Time (Atlantic/ Azores)'
+ ],
+ 'Atlantic/Cape_Verde' => [
+ 'offset' => '-1',
+ 'offset_text' => '[GMT-01:00]',
+ 'name' => 'Cape Verde Time (Atlantic/ Cape Verde)'
+ ],
+ 'Atlantic/South_Georgia' => ['offset' => '-2',
+ 'offset_text' => '[GMT-02:00]',
+ 'name' => 'South Georgia Standard Time (Atlantic/ South Georgia)'
+ ],
+ 'Atlantic/Bermuda' => ['offset' => '-4',
+ 'offset_text' => '[GMT-04:00]',
+ 'name' => 'Atlantic Standard Time (Atlantic/ Bermuda)'
+ ],
+ 'Atlantic/Stanley' => ['offset' => '-4',
+ 'offset_text' => '[GMT-04:00]',
+ 'name' => 'Falkland Is. Time (Atlantic/ Stanley)'
+ ],
+ ],
+ 'Australia' => [
+ 'Australia/Perth' => [
+ 'offset' => '8',
+ 'offset_text' => '[GMT+08:00]',
+ 'name' => 'Western Standard Time (Australia) (Australia/ Perth)'
+ ],
+ 'Australia/Broken_Hill' => ['offset' => '9.5',
+ 'offset_text' => '[GMT+09:30]',
+ 'name' => 'Central Standard Time (Australia/ Broken Hill)'
+ ],
+ 'Australia/Darwin' => ['offset' => '9.5',
+ 'offset_text' => '[GMT+09:30]',
+ 'name' => 'Central Standard Time (Northern Territory) (ACT)'
+ ],
+ 'Australia/Adelaide' => ['offset' => '9.5',
+ 'offset_text' => '[GMT+09:30]',
+ 'name' => 'Central Standard Time (South Australia) (Australia/ Adelaide)'
+ ],
+ 'Australia/Sydney' => ['offset' => '10',
+ 'offset_text' => '[GMT+10:00]',
+ 'name' => 'Eastern Standard Time (New South Wales) (Australia/ Sydney)'
+ ],
+ 'Australia/Brisbane' => ['offset' => '10',
+ 'offset_text' => '[GMT+10:00]',
+ 'name' => 'Eastern Standard Time (Queensland) (Australia/ Brisbane)'
+ ],
+ 'Australia/Hobart' => ['offset' => '10',
+ 'offset_text' => '[GMT+10:00]',
+ 'name' => 'Eastern Standard Time (Tasmania) (Australia/ Hobart)'
+ ],
+ 'Australia/Melbourne' => ['offset' => '10',
+ 'offset_text' => '[GMT+10:00]',
+ 'name' => 'Eastern Standard Time (Victoria) (Australia/ Melbourne)'
+ ],
+ 'Australia/Lord_Howe' => ['offset' => '10.5',
+ 'offset_text' => '[GMT+10:30]',
+ 'name' => 'Load Howe Standard Time (Australia/ Lord Howe)'
+ ],
+ ],
+ 'Europe' => [
+ 'Europe/Lisbon' => [
+ 'offset' => '0',
+ 'offset_text' => '[GMT+00:00]',
+ 'name' => 'Western European Time (Europe/ Lisbon)'
+ ],
+ 'Europe/Berlin' => [
+ 'offset' => '1',
+ 'offset_text' => '[GMT+01:00]',
+ 'name' => 'Central European Time (Europe/ Berlin)'
+ ],
+ 'Europe/Istanbul' => ['offset' => '2',
+ 'offset_text' => '[GMT+02:00]',
+ 'name' => 'Eastern European Time (Europe/ Istanbul)'
+ ],
+ 'Europe/Moscow' => ['offset' => '3',
+ 'offset_text' => '[GMT+03:00]',
+ 'name' => 'Moscow Standard Time (Europe/ Moscow)'
+ ],
+ 'Europe/Samara' => ['offset' => '4',
+ 'offset_text' => '[GMT+04:00]',
+ 'name' => 'Samara Time (Europe/ Samara)'
+ ],
+ ],
+ 'Indian' => [
+ 'Indian/Antananarivo' => ['offset' => '3',
+ 'offset_text' => '[GMT+03:00]',
+ 'name' => 'Antananarivo Time (Indian/ Antananarivo)'
+ ],
+ 'Indian/Comoro' => ['offset' => '3',
+ 'offset_text' => '[GMT+03:00]',
+ 'name' => 'Comoro Time (Indian/ Comoro)'
+ ],
+ 'Indian/Mayotte' => ['offset' => '3',
+ 'offset_text' => '[GMT+03:00]',
+ 'name' => 'Mayotte Time (Indian/ Mayotte)'
+ ],
+ 'Indian/Mauritius' => ['offset' => '4',
+ 'offset_text' => '[GMT+04:00]',
+ 'name' => 'Mauritius Time (Indian/ Mauritius)'
+ ],
+ 'Indian/Reunion' => ['offset' => '4',
+ 'offset_text' => '[GMT+04:00]',
+ 'name' => 'Reunion Time (Indian/ Reunion)'
+ ],
+ 'Indian/Mahe' => ['offset' => '4',
+ 'offset_text' => '[GMT+04:00]',
+ 'name' => 'Seychelles Time (Indian/ Mahe)'
+ ],
+ 'Indian/Kerguelen' => ['offset' => '5',
+ 'offset_text' => '[GMT+05:00]',
+ 'name' => 'French Southern & Antarctic Lands Time (Indian/ Kerguelen)'
+ ],
+ 'Indian/Maldives' => ['offset' => '5',
+ 'offset_text' => '[GMT+05:00]',
+ 'name' => 'Maldives Time (Indian/ Maldives)'
+ ],
+ 'Indian/IST' => ['offset' => '5.5',
+ 'offset_text' => '[GMT+05:30]',
+ 'name' => 'India Standard Time (India Time / IST)'
+ ],
+ 'Indian/Chagos' => ['offset' => '6',
+ 'offset_text' => '[GMT+06:00]',
+ 'name' => 'Indian Ocean Territory Time (Indian/ Chagos)'
+ ],
+ 'Indian/Cocos' => ['offset' => '6.5',
+ 'offset_text' => '[GMT+06:30]',
+ 'name' => 'Cocos Islands Time (Indian/ Cocos)'
+ ],
+ 'Indian/Christmas' => ['offset' => '7',
+ 'offset_text' => '[GMT+07:00]',
+ 'name' => 'Christmas Island Time (Indian/ Christmas)'
+ ],
+ ],
+ 'Pacific Ocean' => [
+ 'Pacific/Palau' => [
+ 'offset' => '9',
+ 'offset_text' => '[GMT+09:00]',
+ 'name' => 'Palau Time (Pacific/ Palau)'
+ ],
+ 'Pacific/Guam' => [
+ 'offset' => '10',
+ 'offset_text' => '[GMT+10:00]',
+ 'name' => 'Chamorro Standard Time (Pacific/ Guam)'
+ ],
+ 'Pacific/Port_Moresby' => ['offset' => '10',
+ 'offset_text' => '[GMT+10:00]',
+ 'name' => 'Papua New Guinea Time (Pacific/ Port Moresby)'
+ ],
+ 'Pacific/Truk' => ['offset' => '10',
+ 'offset_text' => '[GMT+10:00]',
+ 'name' => 'Truk Time (Pacific/ Truk)'
+ ],
+ 'Pacific/Yap' => ['offset' => '10',
+ 'offset_text' => '[GMT+10:00]',
+ 'name' => 'Yap Time (Pacific/ Yap)'
+ ],
+ 'Pacific/Kosrae' => ['offset' => '11',
+ 'offset_text' => '[GMT+11:00]',
+ 'name' => 'Kosrae Time (Pacific/ Kosrae)'
+ ],
+ 'Pacific/Noumea' => ['offset' => '11',
+ 'offset_text' => '[GMT+11:00]',
+ 'name' => 'New Caledonia Time (Pacific/ Noumea)'
+ ],
+ 'Pacific/Ponape' => ['offset' => '11',
+ 'offset_text' => '[GMT+11:00]',
+ 'name' => 'Ponape Time (Pacific/ Ponape)'
+ ],
+ 'Pacific/Efate' => ['offset' => '11',
+ 'offset_text' => '[GMT+11:00]',
+ 'name' => 'Vanuatu Time (Pacific/ Efate)'
+ ],
+ 'Pacific/Norfolk' => ['offset' => '11.5',
+ 'offset_text' => '[GMT+11:30]',
+ 'name' => 'Norfolk Time (Pacific/ Norfolk)'
+ ],
+ 'Pacific/Fiji' => ['offset' => '12',
+ 'offset_text' => '[GMT+12:00]',
+ 'name' => 'Fiji Time (Pacific/ Fiji)'
+ ],
+ 'Pacific/Tarawa' => ['offset' => '12',
+ 'offset_text' => '[GMT+12:00]',
+ 'name' => 'Gilbert Is. Time (Pacific/ Tarawa)'
+ ],
+ 'Pacific/Majuro' => ['offset' => '12',
+ 'offset_text' => '[GMT+12:00]',
+ 'name' => 'Marshall Islands Time (Pacific/ Majuro)'
+ ],
+ 'Pacific/Nauru' => ['offset' => '12',
+ 'offset_text' => '[GMT+12:00]',
+ 'name' => 'Nauru Time (Pacific/ Nauru)'
+ ],
+ 'Pacific/Auckland' => ['offset' => '12',
+ 'offset_text' => '[GMT+12:00]',
+ 'name' => 'New Zealand Standard Time (Pacific/ Auckland)'
+ ],
+ 'Pacific/Funafuti' => ['offset' => '12',
+ 'offset_text' => '[GMT+12:00]',
+ 'name' => 'Tuvalu Time (Pacific/ Funafuti)'
+ ],
+ 'Pacific/Wake' => ['offset' => '12',
+ 'offset_text' => '[GMT+12:00]',
+ 'name' => 'Wake Time (Pacific/ Wake)'
+ ],
+ 'Pacific/Wallis' => ['offset' => '12',
+ 'offset_text' => '[GMT+12:00]',
+ 'name' => 'Wallis & Futuna Time (Pacific/ Wallis)'
+ ],
+ 'Pacific/Chatham' => ['offset' => '12.75',
+ 'offset_text' => '[GMT+12:45]',
+ 'name' => 'Chatham Standard Time (Pacific/ Chatham)'
+ ],
+ 'Pacific/Enderbury' => ['offset' => '13',
+ 'offset_text' => '[GMT+13:00]',
+ 'name' => 'Phoenix Is. Time (Pacific/ Enderbury)'
+ ],
+ 'Pacific/Tongatapu' => ['offset' => '13',
+ 'offset_text' => '[GMT+13:00]',
+ 'name' => 'Tonga Time (Pacific/ Tongatapu)'
+ ],
+ 'Pacific/Kiritimati' => ['offset' => '14',
+ 'offset_text' => '[GMT+14:00]',
+ 'name' => 'Line Is. Time (Pacific/ Kiritimati)'
+ ],
+ 'Pacific/Easter' => ['offset' => '-6',
+ 'offset_text' => '[GMT-06:00]',
+ 'name' => 'Easter Is. Time (Pacific/ Easter)'
+ ],
+ 'Pacific/Galapagos' => ['offset' => '-6',
+ 'offset_text' => '[GMT-06:00]',
+ 'name' => 'Galapagos Time (Pacific/ Galapagos)'
+ ],
+ 'Pacific/Pitcairn' => ['offset' => '-8',
+ 'offset_text' => '[GMT-08:00]',
+ 'name' => 'Pitcairn Standard Time (Pacific/ Pitcairn)'
+ ],
+ 'Pacific/Gambier' => ['offset' => '-9',
+ 'offset_text' => '[GMT-09:00]',
+ 'name' => 'Gambier Time (Pacific/ Gambier)'
+ ],
+ 'Pacific/Marquesas' => ['offset' => '-9.5',
+ 'offset_text' => '[GMT-09:30]',
+ 'name' => 'Marquesas Time (Pacific/ Marquesas)'
+ ],
+ 'Pacific/Rarotonga' => ['offset' => '-10',
+ 'offset_text' => '[GMT-10:00]',
+ 'name' => 'Cook Is. Time (Pacific/ Rarotonga)'
+ ],
+ 'Pacific/Tahiti' => ['offset' => '-10',
+ 'offset_text' => '[GMT-10:00]',
+ 'name' => 'Tahiti Time (Pacific/ Tahiti)'
+ ],
+ 'Pacific/Fakaofo' => ['offset' => '-10',
+ 'offset_text' => '[GMT-10:00]',
+ 'name' => 'Tokelau Time (Pacific/ Fakaofo)'
+ ],
+ 'Pacific/Niue' => ['offset' => '-11',
+ 'offset_text' => '[GMT-11:00]',
+ 'name' => 'Niue Time (Pacific/ Niue)'
+ ],
+ 'Pacific/Apia' => ['offset' => '-11',
+ 'offset_text' => '[GMT-11:00]',
+ 'name' => 'West Samoa Time (MIT)'
+ ],
+ ],
+ ];
+ }
+
}
diff --git a/framework/components/CLogger.php b/framework/components/CLogger.php
index 6938a83..c975565 100644
--- a/framework/components/CLogger.php
+++ b/framework/components/CLogger.php
@@ -18,149 +18,157 @@
class CLogger extends CComponent
{
-
- /** @var string - path to save log files */
- protected $_logPath;
- /** @var int - permissions for log files */
- protected $_filePermissions = 0644;
- /** @var int - log levels */
- protected $_threshold = 1;
- /** @var array - array of threshold levels */
- protected $_thresholdArray = array();
- /** @var string - timestamp format */
- protected $_dateFormat = 'Y-m-d H:i:s';
- /** @var int - log files lifetime in days */
- protected $_lifetime = 30;
- /** @var string - filename extension */
- protected $_fileExtension;
- /** @var bool - whether logger can write or not to the log files */
- protected $_enabled = true;
- /** @var array - array of logging levels */
- protected $_levels = array('error' => 1, 'debug' => 2, 'info' => 3, 'all' => 4);
-
-
- /**
- * Class default constructor
- */
- function __construct()
- {
- $this->_enabled = CConfig::get('log.enable') !== '' ? CConfig::get('log.enable') : false;
- $this->_logPath = APPHP_PATH.DS.(CConfig::get('log.path') !== '' ? CConfig::get('log.path') : 'protected/tmp/logs/');
- $this->_fileExtension = CConfig::exists('log.fileExtension') && CConfig::get('log.fileExtension') !== '' ? ltrim(CConfig::get('log.fileExtension'), '.') : 'php';
- $this->_dateFormat = CConfig::get('log.dateFormat') !== '' ? CConfig::get('log.dateFormat') : '';
- $this->_lifetime = CConfig::get('log.lifetime') !== '' ? CConfig::get('log.lifetime') : '';
- $logThreshold = CConfig::get('log.threshold') !== '' ? CConfig::get('log.threshold') : '';
- $logFilePermissions = CConfig::get('log.filePermissions') !== '' ? CConfig::get('log.filePermissions') : '';
-
- if (!file_exists($this->_logPath)) {
- mkdir($this->_logPath, 0755, true);
- }
-
- if (!is_dir($this->_logPath) || !CFile::isWritable($this->_logPath)) {
- $this->_enabled = false;
- }
-
- if (is_numeric($logThreshold)) {
- $this->_threshold = (int)$logThreshold;
- } elseif (is_array($logThreshold)) {
- $this->_threshold = 0;
- $this->_thresholdArray = array_flip($logThreshold);
- }
-
- if (!empty($this->$logFilePermissions) && is_int($this->$logFilePermissions)) {
- $this->_filePermissions = $this->$logFilePermissions;
- }
- }
-
- /**
- * Returns the instance of object
- * @return current class
- */
- public static function init()
- {
- return parent::init(__CLASS__);
- }
-
- /**
- * Write to log file
- * This function will be called using the system helper CLog::addMessage() method
- * @param string $level The error level: 'error', 'debug' or 'info'
- * @param string $msg The error message
- * @return bool
- */
- public function writeLog($level, $msg = '')
- {
- if ($this->_enabled === false) {
- return false;
- }
-
- if ((!isset($this->_levels[$level]) || ($this->_levels[$level] > $this->_threshold))
- && !isset($this->_thresholdArray[$this->_levels[$level]])) {
- return false;
- }
-
- $filePath = $this->_logPath . 'log-' . date('Y-m-d') . '.' . $this->_fileExtension;
- $message = '';
-
- if (!file_exists($filePath)) {
- $newFile = true;
- // Only add protection to php files
- if ($this->_fileExtension === 'php') {
- $message .= "\n\n";
- }
-
- // Delete old log files
- if (!empty($this->_lifetime) && is_int($this->_lifetime)) {
- CFile::removeDirectoryOldestFile($this->_logPath, $this->_lifetime, array('error.log', 'payments.log'));
- }
- }
-
- // Open or create file for writing at end-of-file
- if (!$fp = @fopen($filePath, 'ab')) {
- return false;
- }
-
- flock($fp, LOCK_EX);
-
- // Instantiating DateTime with microseconds appended to initial date is needed for proper support of this format
- if (strpos($this->_dateFormat, 'u') !== false) {
- $microtimeFull = microtime(true);
- $microtimeShort = sprintf("%06d", ($microtimeFull - floor($microtimeFull)) * 1000000);
- $date = new DateTime(date('Y-m-d H:i:s.' . $microtimeShort, $microtimeFull));
- $date = $date->format($this->_dateFormat);
- } else {
- $date = date($this->_dateFormat);
- }
-
- $message .= $this->_formatLine(strtoupper($level), $date, $msg);
-
- for ($written = 0, $length = strlen($message); $written < $length; $written += $result) {
- if (($result = fwrite($fp, substr($message, $written))) === false) {
- break;
- }
- }
-
- flock($fp, LOCK_UN);
- fclose($fp);
-
- if (isset($newFile) && $newFile === true) {
- chmod($filePath, $this->_filePermissions);
- }
-
- return is_int($result);
- }
-
- /**
- * Format the log line
- * This function is used for extensibility of log formatting
- * @param string $level The error level
- * @param string $date Formatted date string
- * @param string $message The log message
- * @return string
- */
- protected function _formatLine($level, $date, $message = '')
- {
- return $level . ' - ' . $date . ' --> ' . $message . "\n";
- }
-
+
+ /** @var string - path to save log files */
+ protected $_logPath;
+ /** @var int - permissions for log files */
+ protected $_filePermissions = 0644;
+ /** @var int - log levels */
+ protected $_threshold = 1;
+ /** @var array - array of threshold levels */
+ protected $_thresholdArray = [];
+ /** @var string - timestamp format */
+ protected $_dateFormat = 'Y-m-d H:i:s';
+ /** @var int - log files lifetime in days */
+ protected $_lifetime = 30;
+ /** @var string - filename extension */
+ protected $_fileExtension;
+ /** @var bool - whether logger can write or not to the log files */
+ protected $_enabled = true;
+ /** @var array - array of logging levels */
+ protected $_levels = ['error' => 1, 'debug' => 2, 'info' => 3, 'all' => 4];
+
+
+ /**
+ * Class default constructor
+ */
+ function __construct()
+ {
+ $this->_enabled = CConfig::get('log.enable') !== '' ? CConfig::get('log.enable') : false;
+ $this->_logPath = APPHP_PATH.DS.(CConfig::get('log.path') !== '' ? CConfig::get('log.path')
+ : 'protected/tmp/logs/');
+ $this->_fileExtension = CConfig::exists('log.fileExtension') && CConfig::get('log.fileExtension') !== ''
+ ? ltrim(CConfig::get('log.fileExtension'), '.') : 'php';
+ $this->_dateFormat = CConfig::get('log.dateFormat') !== '' ? CConfig::get('log.dateFormat') : '';
+ $this->_lifetime = CConfig::get('log.lifetime') !== '' ? CConfig::get('log.lifetime') : '';
+ $logThreshold = CConfig::get('log.threshold') !== '' ? CConfig::get('log.threshold') : '';
+ $logFilePermissions = CConfig::get('log.filePermissions') !== '' ? CConfig::get('log.filePermissions') : '';
+
+ if ( ! file_exists($this->_logPath)) {
+ mkdir($this->_logPath, 0755, true);
+ }
+
+ if ( ! is_dir($this->_logPath) || ! CFile::isWritable($this->_logPath)) {
+ $this->_enabled = false;
+ }
+
+ if (is_numeric($logThreshold)) {
+ $this->_threshold = (int)$logThreshold;
+ } elseif (is_array($logThreshold)) {
+ $this->_threshold = 0;
+ $this->_thresholdArray = array_flip($logThreshold);
+ }
+
+ if ( ! empty($this->$logFilePermissions) && is_int($this->$logFilePermissions)) {
+ $this->_filePermissions = $this->$logFilePermissions;
+ }
+ }
+
+ /**
+ * Returns the instance of object
+ *
+ * @return current class
+ */
+ public static function init()
+ {
+ return parent::init(__CLASS__);
+ }
+
+ /**
+ * Write to log file
+ * This function will be called using the system helper CLog::addMessage() method
+ *
+ * @param string $level The error level: 'error', 'debug' or 'info'
+ * @param string $msg The error message
+ *
+ * @return bool
+ */
+ public function writeLog($level, $msg = '')
+ {
+ if ($this->_enabled === false) {
+ return false;
+ }
+
+ if (( ! isset($this->_levels[$level]) || ($this->_levels[$level] > $this->_threshold))
+ && ! isset($this->_thresholdArray[$this->_levels[$level]])
+ ) {
+ return false;
+ }
+
+ $filePath = $this->_logPath.'log-'.date('Y-m-d').'.'.$this->_fileExtension;
+ $message = '';
+
+ if ( ! file_exists($filePath)) {
+ $newFile = true;
+ // Only add protection to php files
+ if ($this->_fileExtension === 'php') {
+ $message .= "\n\n";
+ }
+
+ // Delete old log files
+ if ( ! empty($this->_lifetime) && is_int($this->_lifetime)) {
+ CFile::removeDirectoryOldestFile($this->_logPath, $this->_lifetime, ['error.log', 'payments.log']);
+ }
+ }
+
+ // Open or create file for writing at end-of-file
+ if ( ! $fp = @fopen($filePath, 'ab')) {
+ return false;
+ }
+
+ flock($fp, LOCK_EX);
+
+ // Instantiating DateTime with microseconds appended to initial date is needed for proper support of this format
+ if (strpos($this->_dateFormat, 'u') !== false) {
+ $microtimeFull = microtime(true);
+ $microtimeShort = sprintf("%06d", ($microtimeFull - floor($microtimeFull)) * 1000000);
+ $date = new DateTime(date('Y-m-d H:i:s.'.$microtimeShort, $microtimeFull));
+ $date = $date->format($this->_dateFormat);
+ } else {
+ $date = date($this->_dateFormat);
+ }
+
+ $message .= $this->_formatLine(strtoupper($level), $date, $msg);
+
+ for ($written = 0, $length = strlen($message); $written < $length; $written += $result) {
+ if (($result = fwrite($fp, substr($message, $written))) === false) {
+ break;
+ }
+ }
+
+ flock($fp, LOCK_UN);
+ fclose($fp);
+
+ if (isset($newFile) && $newFile === true) {
+ chmod($filePath, $this->_filePermissions);
+ }
+
+ return is_int($result);
+ }
+
+ /**
+ * Format the log line
+ * This function is used for extensibility of log formatting
+ *
+ * @param string $level The error level
+ * @param string $date Formatted date string
+ * @param string $message The log message
+ *
+ * @return string
+ */
+ protected function _formatLine($level, $date, $message = '')
+ {
+ return $level.' - '.$date.' --> '.$message."\n";
+ }
+
}
diff --git a/framework/components/CMessageSource.php b/framework/components/CMessageSource.php
index 630f5da..3edc50c 100644
--- a/framework/components/CMessageSource.php
+++ b/framework/components/CMessageSource.php
@@ -16,97 +16,107 @@
class CMessageSource extends CComponent
{
-
- /** @var string */
- private $_basePath;
- /** @var array */
- private $_messages = array();
-
- /**
- * Class constructor
- * @return void
- */
- function __construct()
- {
- $this->_basePath = dirname(__FILE__);
- }
-
- /**
- * Returns the instance of object
- * @return current class
- */
- public static function init()
- {
- return parent::init(__CLASS__);
- }
-
- /**
- * Loads message translation for specified language and category
- * @param string $category
- * @param string $language
- * @return array the loaded messages
- */
- protected function _loadMessages($category, $language)
- {
- $messages = array();
-
- if ($category == 'core') {
- $messageFile = $this->_basePath . DS . '..' . DS . 'messages' . DS . $language . DS . $category . '.php';
- } elseif ($category == 'i18n') {
- $messageFile = $this->_basePath . DS . '..' . DS . 'i18n' . DS . $language . '.php';
- } elseif ($category == 'setup') {
- $messageFile = APPHP_PATH . DS . 'protected' . DS . 'modules' . DS . $category . DS . 'messages' . DS . $language . DS . $category . '.php';
- } else {
- $messageFile = APPHP_PATH . DS . 'protected' . DS . 'messages' . DS . $language . DS . $category . '.php';
- }
-
- if (is_file($messageFile)) {
- $messages = include($messageFile);
- }
-
- return $messages;
- }
-
- /**
- * Translates a message to the specified language
- * @param string $category
- * @param string $message
- * @param string $language
- * @return string the translated message
- */
- public function translate($category, $message, $language = null)
- {
- if ($language === null) {
- $language = A::app()->getLanguage();
- }
-
- $key = $language . '.' . $category;
- if (!isset($this->_messages[$key])) {
- $this->_messages[$key] = $this->_loadMessages($category, $language);
- }
-
- if (isset($this->_messages[$key][$message])) {
- return $this->_messages[$key][$message];
- } elseif ($pos = strpos($message, '.') !== false) {
- // Check sub-arrays (upto 2 levels)
- $messageParts = explode('.', $message);
- $parts = count($messageParts);
-
- if ($parts == 2) {
- $arrMessages = isset($this->_messages[$key][$messageParts[0]]) ? $this->_messages[$key][$messageParts[0]] : '';
- if (is_array($arrMessages) && isset($arrMessages[$messageParts[1]])) {
- return $arrMessages[$messageParts[1]];
- }
- } elseif ($parts == 3) {
- $arrSubMessages = isset($this->_messages[$key][$messageParts[0]][$messageParts[1]]) ? $this->_messages[$key][$messageParts[0]][$messageParts[1]] : '';
- if (is_array($arrSubMessages) && isset($arrSubMessages[$messageParts[2]])) {
- return $arrSubMessages[$messageParts[2]];
- }
- }
- }
-
- // No message found, return it "as is"
- return (APPHP_MODE == 'debug') ? '@@@' . $message : $message;
- }
-
+
+ /** @var string */
+ private $_basePath;
+ /** @var array */
+ private $_messages = [];
+
+ /**
+ * Class constructor
+ *
+ * @return void
+ */
+ function __construct()
+ {
+ $this->_basePath = dirname(__FILE__);
+ }
+
+ /**
+ * Returns the instance of object
+ *
+ * @return current class
+ */
+ public static function init()
+ {
+ return parent::init(__CLASS__);
+ }
+
+ /**
+ * Loads message translation for specified language and category
+ *
+ * @param string $category
+ * @param string $language
+ *
+ * @return array the loaded messages
+ */
+ protected function _loadMessages($category, $language)
+ {
+ $messages = [];
+
+ if ($category == 'core') {
+ $messageFile = $this->_basePath.DS.'..'.DS.'messages'.DS.$language.DS.$category.'.php';
+ } elseif ($category == 'i18n') {
+ $messageFile = $this->_basePath.DS.'..'.DS.'i18n'.DS.$language.'.php';
+ } elseif ($category == 'setup') {
+ $messageFile = APPHP_PATH.DS.'protected'.DS.'modules'.DS.$category.DS.'messages'.DS.$language.DS.$category.'.php';
+ } else {
+ $messageFile = APPHP_PATH.DS.'protected'.DS.'messages'.DS.$language.DS.$category.'.php';
+ }
+
+ if (is_file($messageFile)) {
+ $messages = include($messageFile);
+ }
+
+ return $messages;
+ }
+
+ /**
+ * Translates a message to the specified language
+ *
+ * @param string $category
+ * @param string $message
+ * @param string $language
+ *
+ * @return string the translated message
+ */
+ public function translate($category, $message, $language = null)
+ {
+ if ($language === null) {
+ $language = A::app()->getLanguage();
+ }
+
+ $key = $language.'.'.$category;
+ if ( ! isset($this->_messages[$key])) {
+ $this->_messages[$key] = $this->_loadMessages($category, $language);
+ }
+
+ if (isset($this->_messages[$key][$message])) {
+ return $this->_messages[$key][$message];
+ } elseif ($pos = strpos($message, '.') !== false) {
+ // Check sub-arrays (upto 2 levels)
+ $messageParts = explode('.', $message);
+ $parts = count($messageParts);
+
+ if ($parts == 2) {
+ $arrMessages = isset($this->_messages[$key][$messageParts[0]])
+ ? $this->_messages[$key][$messageParts[0]]
+ : '';
+ if (is_array($arrMessages) && isset($arrMessages[$messageParts[1]])) {
+ return $arrMessages[$messageParts[1]];
+ }
+ } elseif ($parts == 3) {
+ $arrSubMessages = isset($this->_messages[$key][$messageParts[0]][$messageParts[1]])
+ ? $this->_messages[$key][$messageParts[0]][$messageParts[1]]
+ : '';
+ if (is_array($arrSubMessages) && isset($arrSubMessages[$messageParts[2]])) {
+ return $arrSubMessages[$messageParts[2]];
+ }
+ }
+ }
+
+ // No message found, return it "as is"
+ return (APPHP_MODE == 'debug') ? '@@@'.$message : $message;
+ }
+
}
\ No newline at end of file
diff --git a/framework/components/CMobileDetect.php b/framework/components/CMobileDetect.php
index 2c3a6af..950c05d 100644
--- a/framework/components/CMobileDetect.php
+++ b/framework/components/CMobileDetect.php
@@ -23,29 +23,29 @@
class CMobileDetect extends CComponent
{
- /** @var Mobile_detect */
- static private $_mobileDetect = null;
-
-
- /**
- * Class default constructor
- */
- function __construct()
- {
-
- }
-
- /**
- * Returns the instance of object
- * @return current class
- */
- public static function init()
- {
- if (self::$_mobileDetect == null) {
- self::$_mobileDetect = new Mobile_Detect();
- }
-
- return self::$_mobileDetect;
- }
-
+ /** @var Mobile_detect */
+ static private $_mobileDetect = null;
+
+
+ /**
+ * Class default constructor
+ */
+ function __construct()
+ {
+ }
+
+ /**
+ * Returns the instance of object
+ *
+ * @return CMobileDetect
+ */
+ public static function init()
+ {
+ if (self::$_mobileDetect == null) {
+ self::$_mobileDetect = new Mobile_Detect();
+ }
+
+ return self::$_mobileDetect;
+ }
+
}
\ No newline at end of file
diff --git a/framework/components/CShoppingCart.php b/framework/components/CShoppingCart.php
index 1a00553..efd321e 100644
--- a/framework/components/CShoppingCart.php
+++ b/framework/components/CShoppingCart.php
@@ -29,411 +29,487 @@
class CShoppingCart extends CComponent
{
-
- /**
- * This is the regular expression rules that we use to validate the product ID
- * They are: alpha-numeric, dashes, underscores or periods
- * @var string
- */
- protected $_productIdRules = '\.a-z0-9_-';
- /**
- * This is the regular expression rules that we use to validate the product name
- * alpha-numeric, dashes, underscores, colons or periods
- * @var string
- */
- protected $_productNameRules = '\w \-\.\:';
- /**
- * Allow only safe product names
- * @var bool
- */
- protected $_productNameSafe = true;
- /**
- * Allow UFT-8
- * @var array
- */
- protected $_utf8Enabled = true;
- /**
- * @var array
- */
- protected $_cartContent = array();
-
-
- /**
- * Class default constructor
- */
- function __construct()
- {
- // Grab the shopping cart array from the session and initialize it
- $this->_cartContent = A::app()->getSession()->get('shopping_cart_content', null);
- if ($this->_cartContent === null) {
- $this->_cartContent = array('cart_total' => 0, 'total_items' => 0);
- }
- }
-
- /**
- * Returns the instance of object
- * @return current class
- */
- public static function init()
- {
- return parent::init(__CLASS__);
- }
-
- /**
- * Insert items into the cart and save them
- * Ex.: $items = array(
- * 'id' => 'SKU_123',
- * 'qty' => 1,
- * 'price' => 29.90,
- * 'name' => 'T-Shirt',
- * 'image' => 'images/product.png',
- * 'options' => array('Size' => 'L', 'Color' => 'Red')
- * );
- * @param array $items
- * @return bool
- */
- public function insert($items = array())
- {
- // Check if any cart data was passed
- if (!is_array($items) || count($items) === 0) {
- return false;
- }
-
- // We can insert a single product using a one-dimensional array or multiple products using a multi-dimensional one.
- // The way we determine the array type is by looking for a required array key named "id" at the top level.
- // If it's not found, we will assume it's a multi-dimensional array.
- $saveCart = false;
- if (isset($items['id'])) {
- if (($rowId = $this->_insert($items))) {
- $saveCart = true;
- }
- } else {
- foreach ($items as $val) {
- if (is_array($val) && isset($val['id'])) {
- if ($this->_insert($val)) {
- $saveCart = true;
- }
- }
- }
- }
-
- // Save the shopping cart data if insertion action was successful
- if ($saveCart === true) {
- $this->_saveCart();
- return isset($rowId) ? $rowId : true;
- }
-
- return false;
- }
-
- /**
- * Update the cart - permits the quantity of a given item to be changed
- * Ex.: $data = array(
- * 'rowid' => 'b99ccdf16028f015540f341130b6d8ec',
- * 'qty' => 3
- * ...
- * );
- * @param array $items
- * @return bool
- */
- public function update($items = array())
- {
- // Is any data passed?
- if (!is_array($items) || count($items) === 0) {
- return false;
- }
-
- // You can either update a single product using a one-dimensional array, or multiple products using a multi-dimensional one.
- // The way we determine the array type is by looking for a required array key named "rowid".
- // If it's not found we assume it's a multi-dimensional array
- $saveCart = false;
- if (isset($items['rowid'])) {
- if ($this->_update($items) === true) {
- $saveCart = true;
- }
- } else {
- foreach ($items as $val) {
- if (is_array($val) && isset($val['rowid'])) {
- if ($this->_update($val) === true) {
- $saveCart = true;
- }
- }
- }
- }
-
- // Save the cart data if the insert was successful
- if ($saveCart === true) {
- $this->_saveCart();
- return true;
- }
-
- return false;
- }
-
- /**
- * Remove Item - removes an item from the cart
- * @param int
- * @return bool
- */
- public function remove($rowId)
- {
- // Unset & save
- unset($this->_cartContent[$rowId]);
- $this->_saveCart();
-
- return true;
- }
-
-
- /**
- * Destroy the cart
- * Empties the cart and kills the cart session variable
- * @return void
- */
- public function destroy()
- {
- $this->_cartContent = array('cart_total' => 0, 'total_items' => 0);
- A::app()->getSession()->remove('shopping_cart_content');
- }
-
- /**
- * Cart Total returns count of items in cart
- * @return int
- */
- public function total()
- {
- return $this->_cartContent['cart_total'];
- }
-
- /**
- * Total Items - returns the total item count
- * @return int
- */
- public function totalItems()
- {
- return $this->_cartContent['total_items'];
- }
-
- /**
- * Cart Contents
- * Returns the entire cart array
- * @param bool
- * @return array
- */
- public function contents($newestFirst = false)
- {
- // Do we want the newest item first?
- $cart = ($newestFirst) ? array_reverse($this->_cartContent) : $this->_cartContent;
-
- // Remove these so they don't create a problem when showing the cart table
- if (isset($cart['total_items'])) unset($cart['total_items']);
- if (isset($cart['cart_total'])) unset($cart['cart_total']);
-
- return $cart;
- }
-
- /**
- * Get cart item
- * Returns the details of a specific item in the cart
- * @param string $rowId
- * @return array
- */
- public function getItem($rowId)
- {
- return (in_array($rowId, array('total_items', 'cart_total'), true) || !isset($this->_cartContent[$rowId]))
- ? false
- : $this->_cartContent[$rowId];
- }
-
- /**
- * Checks if cart item exists by row ID
- * @param string $rowId
- * @return bool
- */
- public function itemExists($rowId)
- {
- return (in_array($rowId, array('total_items', 'cart_total'), true) || !isset($this->_cartContent[$rowId]))
- ? false
- : true;
- }
-
- /**
- * Checks if cart item exists
- * @param string $productId
- * @param int $returnType 0 - bool, 1 - rowId
- * @return bool|string
- */
- public function itemExistsByProductId($productId = null, $returnType = 0)
- {
- $return = false;
-
- foreach ($this->_cartContent as $key => $val) {
- // Check if product with such ID exists
- if ($val['id'] == $productId) {
- $return = ($returnType == 1) ? $key : true;
- break;
- }
- }
-
- return $return;
- }
-
- /**
- * Returns true value if the rowId that passed to this function correlates to an item that has options associated with it
- * @param string $rowId
- * @return bool
- */
- public function hasOptions($rowId = '')
- {
- return (isset($this->_cartContent[$rowId]['options']) && count($this->_cartContent[$rowId]['options']) !== 0);
- }
-
- /**
- * Product options
- * Returns the an array of options, for a particular product row ID
- * @param string $rowId
- * @return array
- */
- public function productOptions($rowId = '')
- {
- return isset($this->_cartContent[$rowId]['options']) ? $this->_cartContent[$rowId]['options'] : array();
- }
-
- /**
- * Insert single item into the cart and save them
- * @param array $item
- * @return bool
- */
- protected function _insert($item = array())
- {
- // Check if any cart data was passed
- if (!is_array($item) || count($item) === 0) {
- CDebug::addMessage('errors', 'cart_empty_data', A::t('core', 'The {method} method expects to be passed an array containing data.', array('{method}' => 'CShoppingCart::insert()')));
- return false;
- }
-
- // Does the $item array contain an id, quantity, price, and name? These are required parameters.
- if (!isset($item['id'], $item['qty'], $item['price'], $item['name'])) {
- CDebug::addMessage('errors', 'cart_missing_params', A::t('core', 'The cart array must contain a product ID, quantity, price, and name.'));
- return false;
- }
-
- // Prepare the quantity. It can only be a number and trim any leading zeros
- $item['qty'] = (float)$item['qty'];
-
- // If the quantity is zero or blank there's nothing for us to do
- if ($item['qty'] == 0) {
- return false;
- }
-
- // Validate the product ID. It can only be alpha-numeric, dashes, underscores or periods
- // Not totally sure we should impose this rule, but it seems prudent to standardize IDs.
- // Note: These can be user-specified by setting the $this->_productIdRules variable.
- if (!preg_match('/^[' . $this->_productIdRules . ']+$/i', $item['id'])) {
- CDebug::addMessage('errors', 'cart_wrong_product_id', A::t('core', 'Invalid product ID. The product ID can only contain alpha-numeric characters, dashes underscores and periods.'));
- return false;
- }
-
- // Validate the product name. It can only be alpha-numeric, dashes, underscores, colons or periods.
- // Note: These can be user-specified by setting the $this->_productNameRules variable.
- if ($this->_productNameSafe && !preg_match('/^[' . $this->_productNameRules . ']+$/i' . ($this->_utf8Enabled ? 'u' : ''), $item['name'])) {
- CDebug::addMessage('errors', 'cart_wrong_product_name', A::t('core', 'An invalid name was submitted as the product name: {item}. The name can only contain alpha-numeric characters, dashes, underscores, colons and spaces.', array('{item}' => $item['name'])));
- return false;
- }
-
- // Prep the price. Remove leading zeros and anything that isn't a number or decimal point.
- $item['price'] = (float)$item['price'];
-
- // We now need to create a unique identifier for the item being inserted into the cart.
- // Every time something is added to the cart it is stored in the master cart array.
- // Each row in the cart array, however, must have a unique index that identifies not only
- // a particular product, but makes it possible to store identical products with different options.
- // If no options were submitted so we simply MD5 the product ID.
- if (isset($item['options']) && count($item['options']) > 0) {
- $rowId = md5($item['id'] . serialize($item['options']));
- } else {
- $rowId = md5($item['id']);
- }
-
- // Now that we have our unique "row ID", we'll add our cart items to the master array
- // grab quantity if it's already there and add it on
- $oldQuantity = isset($this->_cartContent[$rowId]['qty']) ? (int)$this->_cartContent[$rowId]['qty'] : 0;
-
- // Re-create the entry, just to make sure our index contains only the data from this submission
- $item['rowid'] = $rowId;
- $item['qty'] += $oldQuantity;
- $this->_cartContent[$rowId] = $item;
-
- return $rowId;
- }
-
- /**
- * Update the cart - permits changing item properties
- * @param array $items
- * @return bool
- */
- protected function _update($items = array())
- {
- // Without these array indexes there is nothing we can do
- if (!isset($items['rowid'], $this->_cartContent[$items['rowid']])) {
- return false;
- }
-
- // Prepare the quantity
- if (isset($items['qty'])) {
- $items['qty'] = (float)$items['qty'];
- // Is the quantity zero - we remove the item from the cart
- // If the quantity is greater than zero - we are update quantity
- if ($items['qty'] == 0) {
- unset($this->_cartContent[$items['rowid']]);
- return true;
- }
- }
-
- // Find updatable keys
- $keys = array_intersect(array_keys($this->_cartContent[$items['rowid']]), array_keys($items));
- // If a price was passed, make sure it contains a valid data
- if (isset($items['price'])) {
- $items['price'] = (float)$items['price'];
- }
-
- // Product ID & name shouldn't be changed
- foreach (array_diff($keys, array('id', 'name')) as $key) {
- $this->_cartContent[$items['rowid']][$key] = $items[$key];
- }
-
- return true;
- }
-
- /**
- * Save the cart array to the session DB
- * @return bool
- */
- protected function _saveCart()
- {
- // Add the individual prices and set the cart sub-total
- $this->_cartContent['total_items'] = $this->_cartContent['cart_total'] = 0;
- foreach ($this->_cartContent as $key => $val) {
- // We make sure the array contains the proper indexes
- if (!is_array($val) || !isset($val['price'], $val['qty'])) {
- continue;
- }
-
- $this->_cartContent['cart_total'] += ($val['price'] * $val['qty']);
- $this->_cartContent['total_items'] += $val['qty'];
- $this->_cartContent[$key]['subtotal'] = ($this->_cartContent[$key]['price'] * $this->_cartContent[$key]['qty']);
- }
-
- // If cart is empty - delete from the session cart content
- if (count($this->_cartContent) <= 2) {
- A::app()->getSession()->remove('shopping_cart_content');
- return false;
- }
-
- // Pass to the session cart content
- A::app()->getSession()->set('shopping_cart_content', $this->_cartContent);
-
- return true;
- }
-
+
+ /**
+ * This is the regular expression rules that we use to validate the product ID
+ * They are: alpha-numeric, dashes, underscores or periods
+ *
+ * @var string
+ */
+ protected $_productIdRules = '\.a-z0-9_-';
+ /**
+ * This is the regular expression rules that we use to validate the product name
+ * alpha-numeric, dashes, underscores, colons or periods
+ *
+ * @var string
+ */
+ protected $_productNameRules = '\w \-\.\:';
+ /**
+ * Allow only safe product names
+ *
+ * @var bool
+ */
+ protected $_productNameSafe = true;
+ /**
+ * Allow UFT-8
+ *
+ * @var array
+ */
+ protected $_utf8Enabled = true;
+ /**
+ * @var array
+ */
+ protected $_cartContent = [];
+
+
+ /**
+ * Class default constructor
+ */
+ function __construct()
+ {
+ // Grab the shopping cart array from the session and initialize it
+ $this->_cartContent = A::app()->getSession()->get('shopping_cart_content', null);
+ if ($this->_cartContent === null) {
+ $this->_cartContent = ['cart_total' => 0, 'total_items' => 0];
+ }
+ }
+
+ /**
+ * Returns the instance of object
+ *
+ * @return current class
+ */
+ public static function init()
+ {
+ return parent::init(__CLASS__);
+ }
+
+ /**
+ * Insert items into the cart and save them
+ * Ex.: $items = array(
+ * 'id' => 'SKU_123',
+ * 'qty' => 1,
+ * 'price' => 29.90,
+ * 'name' => 'T-Shirt',
+ * 'image' => 'images/product.png',
+ * 'options' => array('Size' => 'L', 'Color' => 'Red')
+ * );
+ *
+ * @param array $items
+ *
+ * @return bool
+ */
+ public function insert($items = [])
+ {
+ // Check if any cart data was passed
+ if ( ! is_array($items) || count($items) === 0) {
+ return false;
+ }
+
+ // We can insert a single product using a one-dimensional array or multiple products using a multi-dimensional one.
+ // The way we determine the array type is by looking for a required array key named "id" at the top level.
+ // If it's not found, we will assume it's a multi-dimensional array.
+ $saveCart = false;
+ if (isset($items['id'])) {
+ if (($rowId = $this->_insert($items))) {
+ $saveCart = true;
+ }
+ } else {
+ foreach ($items as $val) {
+ if (is_array($val) && isset($val['id'])) {
+ if ($this->_insert($val)) {
+ $saveCart = true;
+ }
+ }
+ }
+ }
+
+ // Save the shopping cart data if insertion action was successful
+ if ($saveCart === true) {
+ $this->_saveCart();
+
+ return isset($rowId) ? $rowId : true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Update the cart - permits the quantity of a given item to be changed
+ * Ex.: $data = array(
+ * 'rowid' => 'b99ccdf16028f015540f341130b6d8ec',
+ * 'qty' => 3
+ * ...
+ * );
+ *
+ * @param array $items
+ *
+ * @return bool
+ */
+ public function update($items = [])
+ {
+ // Is any data passed?
+ if ( ! is_array($items) || count($items) === 0) {
+ return false;
+ }
+
+ // You can either update a single product using a one-dimensional array, or multiple products using a multi-dimensional one.
+ // The way we determine the array type is by looking for a required array key named "rowid".
+ // If it's not found we assume it's a multi-dimensional array
+ $saveCart = false;
+ if (isset($items['rowid'])) {
+ if ($this->_update($items) === true) {
+ $saveCart = true;
+ }
+ } else {
+ foreach ($items as $val) {
+ if (is_array($val) && isset($val['rowid'])) {
+ if ($this->_update($val) === true) {
+ $saveCart = true;
+ }
+ }
+ }
+ }
+
+ // Save the cart data if the insert was successful
+ if ($saveCart === true) {
+ $this->_saveCart();
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Remove Item - removes an item from the cart
+ *
+ * @param int
+ *
+ * @return bool
+ */
+ public function remove($rowId)
+ {
+ // Unset & save
+ unset($this->_cartContent[$rowId]);
+ $this->_saveCart();
+
+ return true;
+ }
+
+
+ /**
+ * Destroy the cart
+ * Empties the cart and kills the cart session variable
+ *
+ * @return void
+ */
+ public function destroy()
+ {
+ $this->_cartContent = ['cart_total' => 0, 'total_items' => 0];
+ A::app()->getSession()->remove('shopping_cart_content');
+ }
+
+ /**
+ * Cart Total returns count of items in cart
+ *
+ * @return int
+ */
+ public function total()
+ {
+ return $this->_cartContent['cart_total'];
+ }
+
+ /**
+ * Total Items - returns the total item count
+ *
+ * @return int
+ */
+ public function totalItems()
+ {
+ return $this->_cartContent['total_items'];
+ }
+
+ /**
+ * Cart Contents
+ * Returns the entire cart array
+ *
+ * @param bool
+ *
+ * @return array
+ */
+ public function contents($newestFirst = false)
+ {
+ // Do we want the newest item first?
+ $cart = ($newestFirst) ? array_reverse($this->_cartContent) : $this->_cartContent;
+
+ // Remove these so they don't create a problem when showing the cart table
+ if (isset($cart['total_items'])) {
+ unset($cart['total_items']);
+ }
+ if (isset($cart['cart_total'])) {
+ unset($cart['cart_total']);
+ }
+
+ return $cart;
+ }
+
+ /**
+ * Get cart item
+ * Returns the details of a specific item in the cart
+ *
+ * @param string $rowId
+ *
+ * @return array
+ */
+ public function getItem($rowId)
+ {
+ return (in_array($rowId, ['total_items', 'cart_total'], true) || ! isset($this->_cartContent[$rowId]))
+ ? false
+ : $this->_cartContent[$rowId];
+ }
+
+ /**
+ * Checks if cart item exists by row ID
+ *
+ * @param string $rowId
+ *
+ * @return bool
+ */
+ public function itemExists($rowId)
+ {
+ return (in_array($rowId, ['total_items', 'cart_total'], true) || ! isset($this->_cartContent[$rowId]))
+ ? false
+ : true;
+ }
+
+ /**
+ * Checks if cart item exists
+ *
+ * @param string $productId
+ * @param int $returnType 0 - bool, 1 - rowId
+ *
+ * @return bool|string
+ */
+ public function itemExistsByProductId($productId = null, $returnType = 0)
+ {
+ $return = false;
+
+ foreach ($this->_cartContent as $key => $val) {
+ // Check if product with such ID exists
+ if ($val['id'] == $productId) {
+ $return = ($returnType == 1) ? $key : true;
+ break;
+ }
+ }
+
+ return $return;
+ }
+
+ /**
+ * Returns true value if the rowId that passed to this function correlates to an item that has options associated with it
+ *
+ * @param string $rowId
+ *
+ * @return bool
+ */
+ public function hasOptions($rowId = '')
+ {
+ return (isset($this->_cartContent[$rowId]['options']) && count($this->_cartContent[$rowId]['options']) !== 0);
+ }
+
+ /**
+ * Product options
+ * Returns the an array of options, for a particular product row ID
+ *
+ * @param string $rowId
+ *
+ * @return array
+ */
+ public function productOptions($rowId = '')
+ {
+ return isset($this->_cartContent[$rowId]['options']) ? $this->_cartContent[$rowId]['options'] : [];
+ }
+
+ /**
+ * Insert single item into the cart and save them
+ *
+ * @param array $item
+ *
+ * @return bool
+ */
+ protected function _insert($item = [])
+ {
+ // Check if any cart data was passed
+ if ( ! is_array($item) || count($item) === 0) {
+ CDebug::addMessage(
+ 'errors',
+ 'cart_empty_data',
+ A::t(
+ 'core',
+ 'The {method} method expects to be passed an array containing data.',
+ ['{method}' => 'CShoppingCart::insert()']
+ )
+ );
+
+ return false;
+ }
+
+ // Does the $item array contain an id, quantity, price, and name? These are required parameters.
+ if ( ! isset($item['id'], $item['qty'], $item['price'], $item['name'])) {
+ CDebug::addMessage(
+ 'errors',
+ 'cart_missing_params',
+ A::t('core', 'The cart array must contain a product ID, quantity, price, and name.')
+ );
+
+ return false;
+ }
+
+ // Prepare the quantity. It can only be a number and trim any leading zeros
+ $item['qty'] = (float)$item['qty'];
+
+ // If the quantity is zero or blank there's nothing for us to do
+ if ($item['qty'] == 0) {
+ return false;
+ }
+
+ // Validate the product ID. It can only be alpha-numeric, dashes, underscores or periods
+ // Not totally sure we should impose this rule, but it seems prudent to standardize IDs.
+ // Note: These can be user-specified by setting the $this->_productIdRules variable.
+ if ( ! preg_match('/^['.$this->_productIdRules.']+$/i', $item['id'])) {
+ CDebug::addMessage(
+ 'errors',
+ 'cart_wrong_product_id',
+ A::t(
+ 'core',
+ 'Invalid product ID. The product ID can only contain alpha-numeric characters, dashes underscores and periods.'
+ )
+ );
+
+ return false;
+ }
+
+ // Validate the product name. It can only be alpha-numeric, dashes, underscores, colons or periods.
+ // Note: These can be user-specified by setting the $this->_productNameRules variable.
+ if ($this->_productNameSafe
+ && ! preg_match(
+ '/^['.$this->_productNameRules.']+$/i'.($this->_utf8Enabled ? 'u' : ''),
+ $item['name']
+ )
+ ) {
+ CDebug::addMessage(
+ 'errors',
+ 'cart_wrong_product_name',
+ A::t(
+ 'core',
+ 'An invalid name was submitted as the product name: {item}. The name can only contain alpha-numeric characters, dashes, underscores, colons and spaces.',
+ ['{item}' => $item['name']]
+ )
+ );
+
+ return false;
+ }
+
+ // Prep the price. Remove leading zeros and anything that isn't a number or decimal point.
+ $item['price'] = (float)$item['price'];
+
+ // We now need to create a unique identifier for the item being inserted into the cart.
+ // Every time something is added to the cart it is stored in the master cart array.
+ // Each row in the cart array, however, must have a unique index that identifies not only
+ // a particular product, but makes it possible to store identical products with different options.
+ // If no options were submitted so we simply MD5 the product ID.
+ if (isset($item['options']) && count($item['options']) > 0) {
+ $rowId = md5($item['id'].serialize($item['options']));
+ } else {
+ $rowId = md5($item['id']);
+ }
+
+ // Now that we have our unique "row ID", we'll add our cart items to the master array
+ // grab quantity if it's already there and add it on
+ $oldQuantity = isset($this->_cartContent[$rowId]['qty']) ? (int)$this->_cartContent[$rowId]['qty'] : 0;
+
+ // Re-create the entry, just to make sure our index contains only the data from this submission
+ $item['rowid'] = $rowId;
+ $item['qty'] += $oldQuantity;
+ $this->_cartContent[$rowId] = $item;
+
+ return $rowId;
+ }
+
+ /**
+ * Update the cart - permits changing item properties
+ *
+ * @param array $items
+ *
+ * @return bool
+ */
+ protected function _update($items = [])
+ {
+ // Without these array indexes there is nothing we can do
+ if ( ! isset($items['rowid'], $this->_cartContent[$items['rowid']])) {
+ return false;
+ }
+
+ // Prepare the quantity
+ if (isset($items['qty'])) {
+ $items['qty'] = (float)$items['qty'];
+ // Is the quantity zero - we remove the item from the cart
+ // If the quantity is greater than zero - we are update quantity
+ if ($items['qty'] == 0) {
+ unset($this->_cartContent[$items['rowid']]);
+
+ return true;
+ }
+ }
+
+ // Find updatable keys
+ $keys = array_intersect(array_keys($this->_cartContent[$items['rowid']]), array_keys($items));
+ // If a price was passed, make sure it contains a valid data
+ if (isset($items['price'])) {
+ $items['price'] = (float)$items['price'];
+ }
+
+ // Product ID & name shouldn't be changed
+ foreach (array_diff($keys, ['id', 'name']) as $key) {
+ $this->_cartContent[$items['rowid']][$key] = $items[$key];
+ }
+
+ return true;
+ }
+
+ /**
+ * Save the cart array to the session DB
+ *
+ * @return bool
+ */
+ protected function _saveCart()
+ {
+ // Add the individual prices and set the cart sub-total
+ $this->_cartContent['total_items'] = $this->_cartContent['cart_total'] = 0;
+ foreach ($this->_cartContent as $key => $val) {
+ // We make sure the array contains the proper indexes
+ if ( ! is_array($val) || ! isset($val['price'], $val['qty'])) {
+ continue;
+ }
+
+ $this->_cartContent['cart_total'] += ($val['price'] * $val['qty']);
+ $this->_cartContent['total_items'] += $val['qty'];
+ $this->_cartContent[$key]['subtotal'] = ($this->_cartContent[$key]['price']
+ * $this->_cartContent[$key]['qty']);
+ }
+
+ // If cart is empty - delete from the session cart content
+ if (count($this->_cartContent) <= 2) {
+ A::app()->getSession()->remove('shopping_cart_content');
+
+ return false;
+ }
+
+ // Pass to the session cart content
+ A::app()->getSession()->set('shopping_cart_content', $this->_cartContent);
+
+ return true;
+ }
+
}
diff --git a/framework/components/CUri.php b/framework/components/CUri.php
index 3d8e97d..17adf02 100644
--- a/framework/components/CUri.php
+++ b/framework/components/CUri.php
@@ -28,313 +28,346 @@
class CUri extends CComponent
{
-
- /** @var array - list of cached URI segments */
- private $_keyVal = array();
- /** @var string - current URI string */
- private $_uriString = '';
- /** @var array - list of URI segments, starts at 0. */
- private $_segments = array();
- /** @var array - list of routed URI segments, starts at 0. */
- private $_rsegments = array();
- /** @var array - PCRE character group allowed in URI segments */
- private $_permittedUriChars = "a-z 0-9~%.:_\-";
-
-
- /**
- * Class default constructor
- */
- function __construct()
- {
- $this->_uriString = $this->_detectUri();
- $this->_explodeSegments();
- }
-
- /**
- * Returns the instance of object
- * @return current class
- */
- public static function init()
- {
- return parent::init(__CLASS__);
- }
-
- /**
- * Fetch a URI Segment
- * This function returns the URI segment based on the number provided.
- * @param int $n
- * @param bool $noResult
- * @return string
- */
- public function segment($n, $noResult = false)
- {
- return isset($this->_segments[$n]) ? $this->_segments[$n] : $noResult;
- }
-
- /**
- * Total number of segments
- * @return int
- */
- public function totalSegments()
- {
- return count($this->_segments);
- }
-
- /**
- * Total number of routed segments
- * @return int
- */
- public function rTotalSegments()
- {
- return count($this->_rsegments);
- }
-
- /**
- * Fetch the entire URI string
- * @return string
- */
- function uriString()
- {
- return $this->_uriString;
- }
-
- /**
- * Generate a key value pair from the URI string
- * This function generates and associative array of URI data starting
- * at the supplied segment. For example, if this is your URI:
- *
- * example.com/user/search/name/joe/location/UK/gender/male
- *
- * You can use this function to generate an array with this prototype:
- *
- * array (
- * name => joe
- * location => UK
- * gender => male
- * )
- *
- * @param int $n the starting segment number
- * @param array $default an array of default values
- * @return array
- */
- public function uriToAssoc($n = 3, $default = array())
- {
- return $this->_uriToAssoc($n, $default, 'segment');
- }
-
- /**
- * Generate a URI string from an associative array
- * @param array $array an associative array of key/values
- * @return array
- */
- public function assocToUri($array)
- {
- $temp = array();
- foreach ((array)$array as $key => $val) {
- $temp[] = $key;
- $temp[] = $val;
- }
-
- return implode('/', $temp);
- }
-
- /**
- * Fetch a URI Segment and add a trailing slash
- * @param int $n
- * @param string $where
- * @return string
- */
- public function slashSegment($n, $where = 'trailing')
- {
- return $this->_slashSegment($n, $where, 'segment');
- }
-
- /**
- * Fetch a URI Segment and add a trailing slash
- * @param int $n
- * @param string $where
- * @return string
- */
- public function rSlashSegment($n, $where = 'trailing')
- {
- return $this->_slashSegment($n, $where, 'rsegment');
- }
-
- /**
- * Returns segment array
- * @return array
- */
- public function segmentArray()
- {
- return $this->_segments;
- }
-
- /**
- * Returns routed segment array
- * @return array
- */
- public function rSegmentArray()
- {
- return $this->_rsegments;
- }
-
- /**
- * Detects the URI
- * This function will detect the URI automatically and fix the query string if necessary.
- * @return string
- */
- private function _detectUri()
- {
- if (!isset($_SERVER['REQUEST_URI']) || !isset($_SERVER['SCRIPT_NAME'])) {
- return '';
- }
-
- $uri = $_SERVER['REQUEST_URI'];
- if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0) {
- $uri = substr($uri, strlen($_SERVER['SCRIPT_NAME']));
- } elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0) {
- $uri = substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME'])));
- }
-
- // This section ensures that even on servers that require the URI to be in the query string (Nginx)
- // a correct URI is found, and also fixes the QUERY_STRING server var.
- if (strncmp($uri, '?/', 2) === 0) {
- $uri = substr($uri, 2);
- }
-
- if ($uri == '/' || empty($uri)) {
- return '/';
- }
-
- $uri = parse_url($uri, PHP_URL_PATH);
-
- // Do some final cleaning of the URI and return it
- return str_replace(array('//', '../'), '/', trim($uri, '/'));
- }
-
- /**
- * Filter segments for malicious characters
- * @param string $str
- * @return string
- */
- private function _filterUri($str)
- {
- if ($str != '' && $this->_permittedUriChars != '') {
- // preg_quote() in PHP 5.3 escapes -, so the str_replace() and addition of - to preg_quote() is to maintain backwards
- // compatibility as many are unaware of how characters in the _permittedUriChars will be parsed as a regex pattern
- if (!preg_match("|^[" . str_replace(array('\\-', '\-'), '-', preg_quote($this->_permittedUriChars, '-')) . "]+$|i", $str)) {
- CDebug::addMessage('warnings', 'uri-disallowed-characters', A::t('core', 'The URI you submitted has disallowed characters.'));
- }
- }
-
- // Convert programmatic characters to entities
- $bad = array('$', '(', ')', '%28', '%29');
- $good = array('$', '(', ')', '(', ')');
-
- return str_replace($bad, $good, $str);
- }
-
- /**
- * Explode the URI Segments. The individual segments will be stored in the $this->_segments array.
- * @return void
- */
- private function _explodeSegments()
- {
- foreach (explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->_uriString)) as $val) {
- // Filter segments for security
- $val = trim($this->_filterUri($val));
-
- if ($val != '') {
- $this->_segments[] = $val;
- }
- }
- }
-
- /**
- * Generate a key value pair from the URI string or Re-routed URI string
- * @param int $n the starting segment number
- * @param array $default an array of default values
- * @param string $which which array we should use
- * @return array
- */
- function _uriToAssoc($n = 3, $default = array(), $which = 'segment')
- {
- if ($which == 'segment') {
- $total_segments = 'total_segments';
- $segment_array = 'segment_array';
- } else {
- $total_segments = 'total_rsegments';
- $segment_array = 'rsegment_array';
- }
-
- if (!is_numeric($n)) {
- return $default;
- }
-
- if (isset($this->_keyVal[$n])) {
- return $this->_keyVal[$n];
- }
-
- if ($this->$total_segments() < $n) {
- if (count($default) == 0) {
- return array();
- }
-
- $retval = array();
- foreach ($default as $val) {
- $retval[$val] = FALSE;
- }
- return $retval;
- }
-
- $segments = array_slice($this->$segment_array(), ($n - 1));
-
- $i = 0;
- $lastval = '';
- $retval = array();
- foreach ($segments as $seg) {
- if ($i % 2) {
- $retval[$lastval] = $seg;
- } else {
- $retval[$seg] = FALSE;
- $lastval = $seg;
- }
-
- $i++;
- }
-
- if (count($default) > 0) {
- foreach ($default as $val) {
- if (!array_key_exists($val, $retval)) {
- $retval[$val] = FALSE;
- }
- }
- }
-
- // Cache the array for reuse
- $this->_keyVal[$n] = $retval;
- return $retval;
- }
-
- /**
- * Fetch a URI Segment and add a trailing slash - helper function
- * @param int $n
- * @param string $where
- * @param string $which
- * @return string
- */
- private function _slashSegment($n, $where = 'trailing', $which = 'segment')
- {
- $leading = '/';
- $trailing = '/';
-
- if ($where == 'trailing') {
- $leading = '';
- } elseif ($where == 'leading') {
- $trailing = '';
- }
-
- return $leading . $this->$which($n) . $trailing;
- }
-
+
+ /** @var array - list of cached URI segments */
+ private $_keyVal = [];
+ /** @var string - current URI string */
+ private $_uriString = '';
+ /** @var array - list of URI segments, starts at 0. */
+ private $_segments = [];
+ /** @var array - list of routed URI segments, starts at 0. */
+ private $_rsegments = [];
+ /** @var array - PCRE character group allowed in URI segments */
+ private $_permittedUriChars = "a-z 0-9~%.:_\-";
+
+
+ /**
+ * Class default constructor
+ */
+ function __construct()
+ {
+ $this->_uriString = $this->_detectUri();
+ $this->_explodeSegments();
+ }
+
+ /**
+ * Returns the instance of object
+ *
+ * @return current class
+ */
+ public static function init()
+ {
+ return parent::init(__CLASS__);
+ }
+
+ /**
+ * Fetch a URI Segment
+ * This function returns the URI segment based on the number provided.
+ *
+ * @param int $n
+ * @param bool $noResult
+ *
+ * @return string
+ */
+ public function segment($n, $noResult = false)
+ {
+ return isset($this->_segments[$n]) ? $this->_segments[$n] : $noResult;
+ }
+
+ /**
+ * Total number of segments
+ *
+ * @return int
+ */
+ public function totalSegments()
+ {
+ return count($this->_segments);
+ }
+
+ /**
+ * Total number of routed segments
+ *
+ * @return int
+ */
+ public function rTotalSegments()
+ {
+ return count($this->_rsegments);
+ }
+
+ /**
+ * Fetch the entire URI string
+ *
+ * @return string
+ */
+ function uriString()
+ {
+ return $this->_uriString;
+ }
+
+ /**
+ * Generate a key value pair from the URI string
+ * This function generates and associative array of URI data starting
+ * at the supplied segment. For example, if this is your URI:
+ *
+ * example.com/user/search/name/joe/location/UK/gender/male
+ *
+ * You can use this function to generate an array with this prototype:
+ *
+ * array (
+ * name => joe
+ * location => UK
+ * gender => male
+ * )
+ *
+ * @param int $n the starting segment number
+ * @param array $default an array of default values
+ *
+ * @return array
+ */
+ public function uriToAssoc($n = 3, $default = [])
+ {
+ return $this->_uriToAssoc($n, $default, 'segment');
+ }
+
+ /**
+ * Generate a URI string from an associative array
+ *
+ * @param array $array an associative array of key/values
+ *
+ * @return array
+ */
+ public function assocToUri($array)
+ {
+ $temp = [];
+ foreach ((array)$array as $key => $val) {
+ $temp[] = $key;
+ $temp[] = $val;
+ }
+
+ return implode('/', $temp);
+ }
+
+ /**
+ * Fetch a URI Segment and add a trailing slash
+ *
+ * @param int $n
+ * @param string $where
+ *
+ * @return string
+ */
+ public function slashSegment($n, $where = 'trailing')
+ {
+ return $this->_slashSegment($n, $where, 'segment');
+ }
+
+ /**
+ * Fetch a URI Segment and add a trailing slash
+ *
+ * @param int $n
+ * @param string $where
+ *
+ * @return string
+ */
+ public function rSlashSegment($n, $where = 'trailing')
+ {
+ return $this->_slashSegment($n, $where, 'rsegment');
+ }
+
+ /**
+ * Returns segment array
+ *
+ * @return array
+ */
+ public function segmentArray()
+ {
+ return $this->_segments;
+ }
+
+ /**
+ * Returns routed segment array
+ *
+ * @return array
+ */
+ public function rSegmentArray()
+ {
+ return $this->_rsegments;
+ }
+
+ /**
+ * Detects the URI
+ * This function will detect the URI automatically and fix the query string if necessary.
+ *
+ * @return string
+ */
+ private function _detectUri()
+ {
+ if ( ! isset($_SERVER['REQUEST_URI']) || ! isset($_SERVER['SCRIPT_NAME'])) {
+ return '';
+ }
+
+ $uri = $_SERVER['REQUEST_URI'];
+ if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0) {
+ $uri = substr($uri, strlen($_SERVER['SCRIPT_NAME']));
+ } elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0) {
+ $uri = substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME'])));
+ }
+
+ // This section ensures that even on servers that require the URI to be in the query string (Nginx)
+ // a correct URI is found, and also fixes the QUERY_STRING server var.
+ if (strncmp($uri, '?/', 2) === 0) {
+ $uri = substr($uri, 2);
+ }
+
+ if ($uri == '/' || empty($uri)) {
+ return '/';
+ }
+
+ $uri = parse_url($uri, PHP_URL_PATH);
+
+ // Do some final cleaning of the URI and return it
+ return str_replace(['//', '../'], '/', trim($uri, '/'));
+ }
+
+ /**
+ * Filter segments for malicious characters
+ *
+ * @param string $str
+ *
+ * @return string
+ */
+ private function _filterUri($str)
+ {
+ if ($str != '' && $this->_permittedUriChars != '') {
+ // preg_quote() in PHP 5.3 escapes -, so the str_replace() and addition of - to preg_quote() is to maintain backwards
+ // compatibility as many are unaware of how characters in the _permittedUriChars will be parsed as a regex pattern
+ if ( ! preg_match(
+ "|^[".str_replace(['\\-', '\-'], '-', preg_quote($this->_permittedUriChars, '-'))."]+$|i",
+ $str
+ )
+ ) {
+ CDebug::addMessage(
+ 'warnings',
+ 'uri-disallowed-characters',
+ A::t('core', 'The URI you submitted has disallowed characters.')
+ );
+ }
+ }
+
+ // Convert programmatic characters to entities
+ $bad = ['$', '(', ')', '%28', '%29'];
+ $good = ['$', '(', ')', '(', ')'];
+
+ return str_replace($bad, $good, $str);
+ }
+
+ /**
+ * Explode the URI Segments. The individual segments will be stored in the $this->_segments array.
+ *
+ * @return void
+ */
+ private function _explodeSegments()
+ {
+ foreach (explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->_uriString)) as $val) {
+ // Filter segments for security
+ $val = trim($this->_filterUri($val));
+
+ if ($val != '') {
+ $this->_segments[] = $val;
+ }
+ }
+ }
+
+ /**
+ * Generate a key value pair from the URI string or Re-routed URI string
+ *
+ * @param int $n the starting segment number
+ * @param array $default an array of default values
+ * @param string $which which array we should use
+ *
+ * @return array
+ */
+ function _uriToAssoc($n = 3, $default = [], $which = 'segment')
+ {
+ if ($which == 'segment') {
+ $total_segments = 'total_segments';
+ $segment_array = 'segment_array';
+ } else {
+ $total_segments = 'total_rsegments';
+ $segment_array = 'rsegment_array';
+ }
+
+ if ( ! is_numeric($n)) {
+ return $default;
+ }
+
+ if (isset($this->_keyVal[$n])) {
+ return $this->_keyVal[$n];
+ }
+
+ if ($this->$total_segments() < $n) {
+ if (count($default) == 0) {
+ return [];
+ }
+
+ $retval = [];
+ foreach ($default as $val) {
+ $retval[$val] = false;
+ }
+
+ return $retval;
+ }
+
+ $segments = array_slice($this->$segment_array(), ($n - 1));
+
+ $i = 0;
+ $lastval = '';
+ $retval = [];
+ foreach ($segments as $seg) {
+ if ($i % 2) {
+ $retval[$lastval] = $seg;
+ } else {
+ $retval[$seg] = false;
+ $lastval = $seg;
+ }
+
+ $i++;
+ }
+
+ if (count($default) > 0) {
+ foreach ($default as $val) {
+ if ( ! array_key_exists($val, $retval)) {
+ $retval[$val] = false;
+ }
+ }
+ }
+
+ // Cache the array for reuse
+ $this->_keyVal[$n] = $retval;
+
+ return $retval;
+ }
+
+ /**
+ * Fetch a URI Segment and add a trailing slash - helper function
+ *
+ * @param int $n
+ * @param string $where
+ * @param string $which
+ *
+ * @return string
+ */
+ private function _slashSegment($n, $where = 'trailing', $which = 'segment')
+ {
+ $leading = '/';
+ $trailing = '/';
+
+ if ($where == 'trailing') {
+ $leading = '';
+ } elseif ($where == 'leading') {
+ $trailing = '';
+ }
+
+ return $leading.$this->$which($n).$trailing;
+ }
+
}
diff --git a/framework/console/CCacheClearCommand.php b/framework/console/CCacheClearCommand.php
index 288f0ec..c571889 100644
--- a/framework/console/CCacheClearCommand.php
+++ b/framework/console/CCacheClearCommand.php
@@ -19,7 +19,9 @@ class CCacheClearCommand implements IConsoleCommand
/**
* Handle specific console command
+ *
* @param string $param
+ *
* @return string
*/
public static function handle($param = '')
@@ -30,13 +32,13 @@ public static function handle($param = '')
//var_dump($argc);
if (empty($param)) {
- $output .= CConsole::redbg("No cache type for deleting is defined. Type cache:clear -help") . PHP_EOL;
+ $output .= CConsole::redbg("No cache type for deleting is defined. Type cache:clear -help").PHP_EOL;
// } elseif ($param === '-h' || $param === '-help') {
// $output .= CConsole::yellow("Usage:") . PHP_EOL;
// $output .= " cache:clear-all\tFlush all application cache". PHP_EOL;
// $output .= " cache:clear [type]\tFlush specific application cache". PHP_EOL;
// $output .= " \t\t\t[type] - the type of cache to be removed: 'db', 'css', 'js' or 'all'". PHP_EOL;
-// } elseif (in_array($param, array('db', 'css', 'js', 'all'))) {
+// } elseif (in_array($param, ['db', 'css', 'js', 'all'])) {
// if($param == 'db' || $param == 'all'){
// if (CConfig::get('cache.db.path') == '') {
// $output .= CConsole::redbg("Config value 'cache.db.path' is not defined. Check your configuration file.") . PHP_EOL;
diff --git a/framework/console/CConsole.php b/framework/console/CConsole.php
index 46c544d..f3879fb 100644
--- a/framework/console/CConsole.php
+++ b/framework/console/CConsole.php
@@ -30,32 +30,36 @@ class CConsole
*
* @param array $argv
*/
- public function __construct($argv = array())
+ public function __construct($argv = [])
{
$this->argv = $argv;
}
/**
* Get command
+ *
* @return mixed|string
*/
public function getCommand()
{
- return !empty($this->argv[1]) ? $this->argv[1] : '';
+ return ! empty($this->argv[1]) ? $this->argv[1] : '';
}
/**
* Get parameters
+ *
* @return mixed|string
*/
public function getParams()
{
- return !empty($this->argv[2]) ? $this->argv[2] : '';
+ return ! empty($this->argv[2]) ? $this->argv[2] : '';
}
/**
* Draw green line
+ *
* @param string $string
+ *
* @return string
*/
public static function green($string = '')
@@ -65,7 +69,9 @@ public static function green($string = '')
/**
* Draw yellow line
+ *
* @param string $string
+ *
* @return string
*/
public static function yellow($string = '')
@@ -87,11 +93,11 @@ public static function redbg($string = '', $padding = true)
$output = '';
if ($padding) {
- $output .= "\e[0;41m".str_pad(' ', $length, " ", STR_PAD_LEFT)."\e[0m" . PHP_EOL;
+ $output .= "\e[0;41m".str_pad(' ', $length, " ", STR_PAD_LEFT)."\e[0m".PHP_EOL;
}
$output .= "\e[0;41m".($padding ? ' ' : '').$string.($padding ? ' ' : '')."\e[0m".PHP_EOL;
if ($padding) {
- $output .= "\e[0;41m".str_pad(' ', $length, " ", STR_PAD_LEFT)."\e[0m" . PHP_EOL;
+ $output .= "\e[0;41m".str_pad(' ', $length, " ", STR_PAD_LEFT)."\e[0m".PHP_EOL;
}
return $output;
diff --git a/framework/console/CConsoleCommand.php b/framework/console/CConsoleCommand.php
index 9e10aed..905b5c6 100644
--- a/framework/console/CConsoleCommand.php
+++ b/framework/console/CConsoleCommand.php
@@ -32,12 +32,14 @@ class CConsoleCommand
public function __construct($command = '', $param = '')
{
$this->command = $command;
- $this->param = $param;
+ $this->param = $param;
}
/**
* Run command
- * @param bool $return
+ *
+ * @param bool $return
+ *
* @return string
*/
public function run($return = false)
@@ -45,7 +47,6 @@ public function run($return = false)
$output = '';
switch ($this->command) {
-
case '-h':
case '--help':
case 'help':
@@ -72,7 +73,7 @@ public function run($return = false)
default:
$output .= PHP_EOL;
- $output .= CConsole::redbg("Command '".$this->command."' is not defined.") . PHP_EOL;
+ $output .= CConsole::redbg("Command '".$this->command."' is not defined.").PHP_EOL;
$output .= 'Type "bin/aii --help" to check all commands and options.';
break;
}
diff --git a/framework/console/CHelpCommand.php b/framework/console/CHelpCommand.php
index 39677ed..c56f99b 100644
--- a/framework/console/CHelpCommand.php
+++ b/framework/console/CHelpCommand.php
@@ -19,35 +19,36 @@ class CHelpCommand implements IConsoleCommand
/**
* Handle specific console command
+ *
* @return string
*/
public static function handle()
{
$output = '';
- $output .= 'ApPHP Framework ' . CConsole::green(A::version()) . PHP_EOL;
+ $output .= 'ApPHP Framework '.CConsole::green(A::version()).PHP_EOL;
$output .= PHP_EOL;
- $output .= CConsole::yellow("Usage:") . PHP_EOL;
- $output .= " command [options] [arguments]" . PHP_EOL . PHP_EOL;
+ $output .= CConsole::yellow("Usage:").PHP_EOL;
+ $output .= " command [options] [arguments]".PHP_EOL.PHP_EOL;
- $output .= CConsole::yellow("Options:") . PHP_EOL;
- $output .= " ".CConsole::green("-h, --help")."\t\tDisplay this help message". PHP_EOL;
- $output .= " ".CConsole::green("-v, --version")."\t\tDisplay this application version". PHP_EOL;
+ $output .= CConsole::yellow("Options:").PHP_EOL;
+ $output .= " ".CConsole::green("-h, --help")."\t\tDisplay this help message".PHP_EOL;
+ $output .= " ".CConsole::green("-v, --version")."\t\tDisplay this application version".PHP_EOL;
//-q, --quiet Do not output any message
//-n, --no-interaction Do not ask any interactive question
$output .= PHP_EOL;
- $output .= CConsole::yellow("Available commands:") . PHP_EOL;
+ $output .= CConsole::yellow("Available commands:").PHP_EOL;
- $output .= " ".CConsole::green("help")."\t\t\tHelp on console commands". PHP_EOL;
+ $output .= " ".CConsole::green("help")."\t\t\tHelp on console commands".PHP_EOL;
- $output .= CConsole::yellow("cache") . PHP_EOL;
- $output .= " ".CConsole::green("cache:clear")."\t\tFlush specific application cache". PHP_EOL;
- $output .= " ".CConsole::green("cache:clear-all")."\tFlush all application cache". PHP_EOL;
+ $output .= CConsole::yellow("cache").PHP_EOL;
+ $output .= " ".CConsole::green("cache:clear")."\t\tFlush specific application cache".PHP_EOL;
+ $output .= " ".CConsole::green("cache:clear-all")."\tFlush all application cache".PHP_EOL;
- $output .= CConsole::yellow("make") . PHP_EOL;
- $output .= " ".CConsole::green("make:controller")."\tCreate controller". PHP_EOL;
+ $output .= CConsole::yellow("make").PHP_EOL;
+ $output .= " ".CConsole::green("make:controller")."\tCreate controller".PHP_EOL;
return $output;
}
diff --git a/framework/console/CMakeControllerCommand.php b/framework/console/CMakeControllerCommand.php
index cced01f..f74f6b6 100644
--- a/framework/console/CMakeControllerCommand.php
+++ b/framework/console/CMakeControllerCommand.php
@@ -26,7 +26,7 @@ class CMakeControllerCommand implements IConsoleCommand
*/
public static function handle($param = '')
{
- $output = 'Controller created: ' . $param;
+ $output = 'Controller created: '.$param;
return $output;
}
diff --git a/framework/console/CVersionCommand.php b/framework/console/CVersionCommand.php
index 11c0e5b..ef8953a 100644
--- a/framework/console/CVersionCommand.php
+++ b/framework/console/CVersionCommand.php
@@ -19,11 +19,12 @@ class CVersionCommand implements IConsoleCommand
/**
* Handle specific console command
+ *
* @return string
*/
public static function handle()
{
- $output = 'ApPHP Framework ' . CConsole::green(A::version());
+ $output = 'ApPHP Framework '.CConsole::green(A::version());
return $output;
}
diff --git a/framework/db/CActiveRecord.php b/framework/db/CActiveRecord.php
index cb2a0a7..797c3d1 100644
--- a/framework/db/CActiveRecord.php
+++ b/framework/db/CActiveRecord.php
@@ -77,127 +77,144 @@
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 '';
- }
- }
+ /** @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
+ *
+ * @param string $index
+ *
* @return bool
*/
public function __isset($index)
@@ -205,384 +222,444 @@ 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 = 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
+ /**
+ * 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;
- }
+ 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(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.: chunk(CConfig::get('db.prefix').$this->_tableTranslation.'.news_text LIKE :keywords', array(':keywords'=>'%'.$keywords.'%'));
+ * 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
@@ -593,25 +670,25 @@ private function _createObjectFromTable()
*/
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;
+ 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;
+ $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;
+ $count = 0;
- while ($result = $this->findAll($conditions, $params)){
+ while ($result = $this->findAll($conditions, $params)) {
$callback($result);
- $from += $size;
+ $from += $size;
$conditions['limit'] = "$from, $size";
$count += $size;
@@ -620,1023 +697,1167 @@ public function chunk(array $conditions = [], array $params = [], int $size = 0,
}
}
} else {
- CDebug::AddMessage('errors', 'chunk', A::t('core', 'Wrong params for chunk size: {size} or callback method is callable.', array('{size}' => $size)));
+ 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', 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);
+ * 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'], 'params'=>[':postID'=>10, ':isActive'=>1]);
*
- * @param array $attributes
* @param mixed $conditions
* @param array $params
+ * @param bool|string $cacheId
*
- * @return mixed
+ * @return object
*/
- 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 . '
- 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();
- }
+ 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'), '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 = [], $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
+ *
+ * @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 = [], $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 367a310..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 . ' 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, 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 f2a1c15..04ccb4f 100644
--- a/framework/db/CRecordEntity.php
+++ b/framework/db/CRecordEntity.php
@@ -32,58 +32,74 @@
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 '';
- }
- }
+ /** @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
+ *
+ * @param string $index
+ *
* @return bool
*/
public function __isset($index)
@@ -91,150 +107,177 @@ 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}', 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;
- }
+ /**
+ * 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/widgets/CBreadCrumbs.php b/framework/helpers/widgets/CBreadCrumbs.php
index 09d1a4d..aa3667e 100644
--- a/framework/helpers/widgets/CBreadCrumbs.php
+++ b/framework/helpers/widgets/CBreadCrumbs.php
@@ -16,62 +16,79 @@
class CBreadCrumbs extends CWidgs
{
-
- const NL = "\n";
-
- /**
- * Draws breadcrumbs
- * @param array $params
- *
- * Usage:
- * CWidget::create('CBreadCrumbs', array(
- * 'links' => array(
- * array('label'=>'Label A', 'url'=>'url1/'),
- * array('label'=>'Label B', 'url'=>'url2/'),
- * ),
- * 'wrapperClass' => '',
- * 'wrapperTag' => '',
- * 'linkWrapperTag' => '',
- * 'separator' => ' / ',
- * 'return' => true
- * ));
- */
- public static function init($params = array())
- {
- parent::init($params);
-
- $output = '';
- $wrapperTag = self::params('wrapperTag', 'div');
- $links = self::params('links', '');
- $linkWrapperTag = self::params('linkWrapperTag', '');
- $separator = self::params('separator', '»');
- $return = (bool)self::params('return', true);
- $wrapperClass = self::params('wrapperClass', 'breadcrumbs');
- $htmlOptions = array('class' => $wrapperClass);
-
- if (is_array($links)) {
- if (!empty($wrapperTag)) $output .= CHtml::openTag($wrapperTag, $htmlOptions) . self::NL;
- $counter = 0;
- foreach ($params['links'] as $item => $val) {
- $url = self::keyAt('url', $val, '');
- $label = self::keyAt('label', $val, '');
-
- if (!empty($linkWrapperTag)) $output .= CHtml::openTag($linkWrapperTag, array()) . self::NL;
-
- if ($counter) $output .= ' ' . $separator . ' ';
- if (!empty($url)) $output .= CHtml::link($label, $url);
- else $output .= CHtml::tag('span', array(), $label) . self::NL;
-
- if (!empty($linkWrapperTag)) $output .= CHtml::closeTag($linkWrapperTag) . self::NL;
-
- $counter++;
- }
-
- if (!empty($wrapperTag)) $output .= CHtml::closeTag($wrapperTag) . self::NL;
- }
-
- if ($return) return $output;
- else echo $output;
- }
-
+
+ const NL = "\n";
+
+ /**
+ * Draws breadcrumbs
+ *
+ * @param array $params
+ *
+ * Usage:
+ * CWidget::create('CBreadCrumbs', [
+ * 'links' => array(
+ * ['label'=>'Label A', 'url'=>'url1/'],
+ * ['label'=>'Label B', 'url'=>'url2/'],
+ * ),
+ * 'wrapperClass' => '',
+ * 'wrapperTag' => '',
+ * 'linkWrapperTag' => '',
+ * 'separator' => ' / ',
+ * 'return' => true
+ * ]);
+ */
+ public static function init($params = [])
+ {
+ parent::init($params);
+
+ $output = '';
+ $wrapperTag = self::params('wrapperTag', 'div');
+ $links = self::params('links', '');
+ $linkWrapperTag = self::params('linkWrapperTag', '');
+ $separator = self::params('separator', '»');
+ $return = (bool)self::params('return', true);
+ $wrapperClass = self::params('wrapperClass', 'breadcrumbs');
+ $htmlOptions = ['class' => $wrapperClass];
+
+ if (is_array($links)) {
+ if ( ! empty($wrapperTag)) {
+ $output .= CHtml::openTag($wrapperTag, $htmlOptions).self::NL;
+ }
+ $counter = 0;
+ foreach ($params['links'] as $item => $val) {
+ $url = self::keyAt('url', $val, '');
+ $label = self::keyAt('label', $val, '');
+
+ if ( ! empty($linkWrapperTag)) {
+ $output .= CHtml::openTag($linkWrapperTag, []).self::NL;
+ }
+
+ if ($counter) {
+ $output .= ' '.$separator.' ';
+ }
+ if ( ! empty($url)) {
+ $output .= CHtml::link($label, $url);
+ } else {
+ $output .= CHtml::tag('span', [], $label).self::NL;
+ }
+
+ if ( ! empty($linkWrapperTag)) {
+ $output .= CHtml::closeTag($linkWrapperTag).self::NL;
+ }
+
+ $counter++;
+ }
+
+ if ( ! empty($wrapperTag)) {
+ $output .= CHtml::closeTag($wrapperTag).self::NL;
+ }
+ }
+
+ if ($return) {
+ return $output;
+ } else {
+ echo $output;
+ }
+ }
+
}
\ No newline at end of file
diff --git a/framework/helpers/widgets/CLanguageSelector.php b/framework/helpers/widgets/CLanguageSelector.php
index 8f7fc48..0bbe429 100644
--- a/framework/helpers/widgets/CLanguageSelector.php
+++ b/framework/helpers/widgets/CLanguageSelector.php
@@ -16,108 +16,119 @@
class CLanguageSelector extends CWidgs
{
-
- /**
- * @const string new line
- */
- const NL = "\n";
-
-
- /**
- * Draws language selector
- * @param array $params
- *
- * Usage:
- * echo CWidget::create('CLanguageSelector', array(
- * 'languages' => array('en'=>array('name'=>'English', 'icon'=>''), 'es'=>array('name'=>'Espanol', 'icon'=>''), 'fr'=>array('name'=>'Francias', 'icon'=>'')),
- * 'display' => 'names|keys|icons|dropdown|list',
- * 'imagesPath' => 'images/langs/',
- * 'forceDrawing' => false,
- * 'currentLanguage' => A::app()->getLanguage(),
- * 'return' => true
- * ));
- */
- public static function init($params = array())
- {
- parent::init($params);
-
- $output = '';
- $tagName = 'div';
- $languages = self::params('languages', array());
- $display = self::params('display', 'names');
- $imagesPath = self::params('imagesPath', '');
- $currentLang = self::params('currentLanguage', '');
- $forceDrawing = self::params('forceDrawing', false);
- $class = self::params('class', '');
- $return = (bool)self::params('return', true);
-
- $totalLangs = count($languages);
- if ($totalLangs == 1 && !$forceDrawing) {
- return '';
- } elseif ($totalLangs < 6 && in_array($display, array('names', 'keys', 'icons'))) {
- // Render options
- $totalLanguages = count($languages);
- $count = 0;
- foreach ($languages as $key => $lang) {
- $langName = isset($lang['name']) ? $lang['name'] : '';
- $langIcon = (isset($lang['icon']) && !empty($lang['icon']) && file_exists($imagesPath . $lang['icon'])) ? $lang['icon'] : 'no_image.png';
-
- if ($display == 'names') {
- $displayValue = $langName;
- } elseif ($display == 'icons') {
- $displayValue = '
- 'categories/insert',
'method' => 'post',
- 'htmlOptions' => array(
+ 'htmlOptions' => [
'name' => 'frmAddCategory',
- ),
- 'fields' => array(
- 'act' => array('type' => 'hidden', 'value' => 'send'),
- 'categoryName' => array('type' => 'textbox', 'title' => 'Category Name', 'mandatoryStar' => true, 'htmlOptions' => array('maxlength' => '50', 'class' => 'text_header')),
- ),
- 'buttons' => array(
- 'cancel' => array('type' => 'button', 'value' => 'Cancel', 'htmlOptions' => array('name' => '', 'onclick' => "$(location).attr('href','categories/index');")),
- 'submit' => array('type' => 'submit', 'value' => 'Create'),
- ),
- 'events' => array(
- 'focus' => array('field' => $errorField),
- ),
+ ],
+ 'fields' => [
+ 'act' => ['type' => 'hidden', 'value' => 'send'],
+ 'categoryName' => ['type' => 'textbox', 'title' => 'Category Name', 'mandatoryStar' => true, 'htmlOptions' => ['maxlength' => '50', 'class' => 'text_header']],
+ ],
+ 'buttons' => [
+ 'cancel' => ['type' => 'button', 'value' => 'Cancel', 'htmlOptions' => ['name' => '', 'onclick' => "$(location).attr('href','categories/index');"]],
+ 'submit' => ['type' => 'submit', 'value' => 'Create'],
+ ],
+ 'events' => [
+ 'focus' => ['field' => $errorField],
+ ],
'return' => true,
- ));
- ?>
+ ]);
+ ?>
This page provides you possibility to add new category.
diff --git a/demos/simple-blog/protected/views/categories/edit.php b/demos/simple-blog/protected/views/categories/edit.php
index 3b6688e..dc407ec 100644
--- a/demos/simple-blog/protected/views/categories/edit.php
+++ b/demos/simple-blog/protected/views/categories/edit.php
@@ -9,9 +9,9 @@
echo CWidget::create('CFormView', array(
'action' => 'categories/update',
'method' => 'post',
- 'htmlOptions' => array(
- 'name' => 'frmEditCategory',
- ),
+ 'htmlOptions' => [
+ 'name' => 'frmEditCategory',
+ ],
'fields' => array(
'act' => array('type' => 'hidden', 'value' => 'send'),
'categoryId' => array('type' => 'hidden', 'value' => $categoryId),
@@ -20,12 +20,12 @@
),
'buttons' => array(
'cancel' => array('type' => 'button', 'value' => 'Cancel', 'htmlOptions' => array('name' => '', 'onclick' => "$(location).attr('href','categories/index');")),
- 'submit' => array('type' => 'submit', 'value' => 'Update'),
- ),
- 'events' => array(
- 'focus' => array('field' => $errorField),
- ),
- 'return' => true,
+ 'submit' => ['type' => 'submit', 'value' => 'Update'],
+ ),
+ 'events' => array(
+ 'focus' => ['field' => $errorField],
+ ),
+ 'return' => true,
));
?>
diff --git a/demos/simple-blog/protected/views/settings/edit.php b/demos/simple-blog/protected/views/settings/edit.php
index 6c865a5..0d82997 100644
--- a/demos/simple-blog/protected/views/settings/edit.php
+++ b/demos/simple-blog/protected/views/settings/edit.php
@@ -4,33 +4,33 @@
= $actionMessage; ?>
- 'settings/update',
'method' => 'post',
- 'htmlOptions' => array(
+ 'htmlOptions' => [
'name' => 'frmSettings',
- ),
- 'fields' => array(
- 'act' => array('type' => 'hidden', 'value' => 'send'),
- 'blogName' => array('type' => 'textbox', 'value' => $blogName, 'title' => 'Blog Name', 'mandatoryStar' => true, 'htmlOptions' => array('maxlength' => '100', 'class' => 'text_header')),
- 'slogan' => array('type' => 'textarea', 'value' => $slogan, 'title' => 'Slogan', 'mandatoryStar' => false, 'htmlOptions' => array('maxlength' => '250', 'class' => 'small')),
- 'footer' => array('type' => 'textarea', 'value' => $footer, 'title' => 'Footer', 'mandatoryStar' => false, 'htmlOptions' => array('maxlength' => '250', 'class' => 'middle')),
- 'postMaxChars' => array('type' => 'textbox', 'value' => $postMaxChars, 'title' => 'Maximum Post Length', 'mandatoryStar' => true, 'htmlOptions' => array('maxlength' => '5', 'class' => 'numeric')),
- 'metaTagTitle' => array('type' => 'textarea', 'value' => $metaTagTitle, 'title' => CHtml::encode('Tag
'), 'mandatoryStar' => true, 'htmlOptions' => array('maxlength' => '250', 'class' => 'small')),
- 'metaTagKeywords' => array('type' => 'textarea', 'value' => $metaTagKeywords, 'title' => CHtml::encode('Meta Tag '), 'mandatoryStar' => false, 'htmlOptions' => array('maxlength' => '250', 'class' => 'middle')),
- 'metaTagDescription' => array('type' => 'textarea', 'value' => $metaTagDescription, 'title' => CHtml::encode('Meta Tag '), 'mandatoryStar' => false, 'htmlOptions' => array('maxlength' => '250', 'class' => 'middle')),
- ),
- 'buttons' => array(
- 'reset' => array('type' => 'reset', 'value' => 'Reset', 'htmlOptions' => array('type' => 'reset')),
- 'submit' => array('type' => 'submit', 'value' => 'Update'),
- ),
- 'events' => array(
- 'focus' => array('field' => $errorField),
- ),
+ ],
+ 'fields' => [
+ 'act' => ['type' => 'hidden', 'value' => 'send'],
+ 'blogName' => ['type' => 'textbox', 'value' => $blogName, 'title' => 'Blog Name', 'mandatoryStar' => true, 'htmlOptions' => ['maxlength' => '100', 'class' => 'text_header']],
+ 'slogan' => ['type' => 'textarea', 'value' => $slogan, 'title' => 'Slogan', 'mandatoryStar' => false, 'htmlOptions' => ['maxlength' => '250', 'class' => 'small']],
+ 'footer' => ['type' => 'textarea', 'value' => $footer, 'title' => 'Footer', 'mandatoryStar' => false, 'htmlOptions' => ['maxlength' => '250', 'class' => 'middle']],
+ 'postMaxChars' => ['type' => 'textbox', 'value' => $postMaxChars, 'title' => 'Maximum Post Length', 'mandatoryStar' => true, 'htmlOptions' => ['maxlength' => '5', 'class' => 'numeric']],
+ 'metaTagTitle' => ['type' => 'textarea', 'value' => $metaTagTitle, 'title' => CHtml::encode('Tag '), 'mandatoryStar' => true, 'htmlOptions' => ['maxlength' => '250', 'class' => 'small']],
+ 'metaTagKeywords' => ['type' => 'textarea', 'value' => $metaTagKeywords, 'title' => CHtml::encode('Meta Tag '), 'mandatoryStar' => false, 'htmlOptions' => ['maxlength' => '250', 'class' => 'middle']],
+ 'metaTagDescription' => ['type' => 'textarea', 'value' => $metaTagDescription, 'title' => CHtml::encode('Meta Tag '), 'mandatoryStar' => false, 'htmlOptions' => ['maxlength' => '250', 'class' => 'middle']],
+ ],
+ 'buttons' => [
+ 'reset' => ['type' => 'reset', 'value' => 'Reset', 'htmlOptions' => ['type' => 'reset']],
+ 'submit' => ['type' => 'submit', 'value' => 'Update'],
+ ],
+ 'events' => [
+ 'focus' => ['field' => $errorField],
+ ],
'return' => true,
- ));
- ?>
+ ]);
+ ?>
This page provides you possibility to edit global site settings.
diff --git a/demos/simple-cms/protected/modules/setup/templates/default.php b/demos/simple-cms/protected/modules/setup/templates/default.php
index 1a3982a..a80be54 100644
--- a/demos/simple-cms/protected/modules/setup/templates/default.php
+++ b/demos/simple-cms/protected/modules/setup/templates/default.php
@@ -27,21 +27,24 @@
'vertical',
- 'items'=>array(
- array('label'=>'1. Server Requirements', 'url'=>'setup/index', 'readonly'=>true),
- array('label'=>'2. Database Settings', 'url'=>'setup/database', 'readonly'=>true),
- array('label'=>'3. Administrator Account', 'url'=>'setup/administrator', 'readonly'=>true),
- array('label'=>'4. Ready to Install', 'url'=>'setup/ready', 'readonly'=>true),
- array('label'=>'5. Completed', 'url'=>'setup/completed', 'readonly'=>true),
- ),
- 'selected'=>$this->_activeMenu,
- 'return'=>false
- ));
+ CWidget::create(
+ 'CMenu',
+ [
+ 'type' => 'vertical',
+ 'items' => [
+ ['label' => '1. Server Requirements', 'url' => 'setup/index', 'readonly' => true],
+ ['label' => '2. Database Settings', 'url' => 'setup/database', 'readonly' => true],
+ ['label' => '3. Administrator Account', 'url' => 'setup/administrator', 'readonly' => true],
+ ['label' => '4. Ready to Install', 'url' => 'setup/ready', 'readonly' => true],
+ ['label' => '5. Completed', 'url' => 'setup/completed', 'readonly' => true],
+ ],
+ 'selected' => $this->_activeMenu,
+ 'return' => false
+ ]
+ );
?>
-
-