1: <?php
2: /**
3: * This code is licensed under AGPLv3 license or Afterlogic Software License
4: * if commercial version of the product was purchased.
5: * For full statements of the licenses see LICENSE-AFTERLOGIC and LICENSE-AGPL3 files.
6: */
7:
8: namespace Aurora\System;
9:
10: /**
11: * @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
12: * @license https://afterlogic.com/products/common-licensing Afterlogic Software License
13: * @copyright Copyright (c) 2019, Afterlogic Corp.
14: *
15: * @package Api
16: */
17: abstract class AbstractContainer
18: {
19: public const SESSION_CONTAINER_PREFIX = 'sess_object_container';
20:
21: /**
22: * @var bool
23: */
24: public $__USE_TRIM_IN_STRINGS__;
25:
26: /**
27: * @var string
28: */
29: protected $sParentClassName;
30:
31: /**
32: * @var string
33: */
34: protected $sSessionUniqueProperty;
35:
36: /**
37: * @var array
38: */
39: protected $aContainer;
40:
41: /**
42: * @var array
43: */
44: protected $aObsolete;
45:
46: /**
47: * @var array
48: */
49: protected $aTrimer;
50:
51: /**
52: * @var array
53: */
54: protected $aLower;
55:
56: /**
57: * @var array
58: */
59: protected $aUpper;
60:
61: /**
62: * @param string $sParentClassName
63: * @param string $sSessionUniqueProperty = ''
64: */
65: public function __construct($sParentClassName, $sSessionUniqueProperty = '')
66: {
67: $this->__USE_TRIM_IN_STRINGS__ = false;
68: $this->sParentClassName = $sParentClassName;
69: $this->sSessionUniqueProperty = $sSessionUniqueProperty;
70:
71: $this->aContainer = array();
72: $this->aObsolete = array();
73: $this->aTrimer = array();
74: $this->aLower = array();
75: $this->aUpper = array();
76: }
77:
78: /**
79: * @param array $aValues
80: * @return void
81: */
82: public function SetDefaults($aValues)
83: {
84: $this->MassSetValues($aValues);
85: $this->FlushObsolete();
86: }
87:
88: /**
89: * @param array $aValues
90: * @return void
91: */
92: public function SetTrimer($aValues)
93: {
94: $this->aTrimer = $aValues;
95: }
96:
97: /**
98: * @param array $aValues
99: * @return void
100: */
101: public function SetLower($aValues)
102: {
103: $this->aLower = $aValues;
104: }
105:
106: /**
107: * @param array $aValues
108: * @return void
109: */
110: public function SetUpper($aValues)
111: {
112: $this->aUpper = $aValues;
113: }
114:
115: /**
116: * @param array $aValues
117: * @return void
118: */
119: public function MassSetValues($aValues)
120: {
121: foreach ($aValues as $sKey => $mValue) {
122: $this->{$sKey} = $mValue;
123: }
124: }
125:
126: /**
127: * @param stdClass $oRow
128: */
129: public function InitByDbRow($oRow)
130: {
131: $aMap = $this->getMap();
132: foreach ($aMap as $sKey => $aTypes) {
133: if (isset($aTypes[1]) && \property_exists($oRow, $aTypes[1])) {
134: if ('password' === $aTypes[0]) {
135: $this->{$sKey} = Utils::DecryptValue($oRow->{$aTypes[1]});
136: } elseif ('datetime' === $aTypes[0]) {
137: $iDateTime = 0;
138: $aDateTime = Utils::DateParse($oRow->{$aTypes[1]});
139: if (\is_array($aDateTime)) {
140: $iDateTime = \gmmktime(
141: $aDateTime['hour'],
142: $aDateTime['minute'],
143: $aDateTime['second'],
144: $aDateTime['month'],
145: $aDateTime['day'],
146: $aDateTime['year']
147: );
148:
149: if (false === $iDateTime || $iDateTime <= 0) {
150: $iDateTime = 0;
151: }
152: }
153:
154: $this->{$sKey} = $iDateTime;
155: } elseif ('serialize' === $aTypes[0]) {
156: $this->{$sKey} = ('' === $oRow->{$aTypes[1]} || !\is_string($oRow->{$aTypes[1]})) ?
157: '' : \unserialize($oRow->{$aTypes[1]});
158: } else {
159: $this->{$sKey} = $oRow->{$aTypes[1]};
160: }
161:
162: $this->FlushObsolete($sKey);
163: }
164: }
165: }
166:
167: /**
168: * @param string $sKey
169: * @return mixed
170: */
171: public function GetObsoleteValue($sKey)
172: {
173: if (\key_exists($sKey, $this->aObsolete)) {
174: return $this->aObsolete[$sKey];
175: }
176:
177: return null;
178: }
179:
180: /**
181: * @param string $sKey = null
182: * @return void
183: */
184: public function FlushObsolete($nsKey = null)
185: {
186: if (null === $nsKey) {
187: $this->aObsolete = array();
188: } else {
189: if (\key_exists($nsKey, $this->aObsolete)) {
190: unset($this->aObsolete[$nsKey]);
191: }
192: }
193: }
194:
195: /**
196: * @param string $sKey
197: * @param mixed $mDefault
198: * @return mixed
199: */
200: public function GetSessionValue($sKey, $mDefault = null)
201: {
202: $aValues = Session::get($this->getSessionUniqueKey(), null);
203:
204: return (\is_array($aValues) && \array_key_exists($sKey, $aValues)) ? $aValues[$sKey] : $mDefault;
205: }
206:
207: /**
208: * @param string $sKey
209: * @param mixed $mValue
210: */
211: public function SetSessionValue($sKey, $mValue)
212: {
213: $sUniqueKey = $this->getSessionUniqueKey();
214: $aValues = Session::get($sUniqueKey, array());
215: if (!\is_array($aValues)) {
216: $aValues = array();
217: }
218:
219: $aValues[$sKey] = $mValue;
220: Session::Set($sUniqueKey, $aValues);
221: }
222:
223: /**
224: * @return string
225: */
226: protected function getSessionUniqueKey()
227: {
228: $sUniqueKey = (0 === \strlen($this->sSessionUniqueProperty)) ? '' : $this->{$this->sSessionUniqueProperty};
229: return AbstractContainer::SESSION_CONTAINER_PREFIX.$this->sParentClassName.$sUniqueKey;
230: }
231:
232: /**
233: * @return string
234: */
235: public function SessionUniqueProperty()
236: {
237: return $this->sSessionUniqueProperty;
238: }
239:
240: /**
241: * @return string
242: */
243: public function SessionUniquePropertyValue()
244: {
245: return (0 === \strlen($this->sSessionUniqueProperty)) ? '' : $this->{$this->sSessionUniqueProperty};
246: }
247:
248: /**
249: * @param string $sPropertyName
250: * @param mixed $mValue
251: * @return bool
252: */
253: public function IsProperty($sPropertyName)
254: {
255: $aMap = $this->getMap();
256: return isset($aMap[$sPropertyName]);
257: }
258:
259: /**
260: * @param string $sPropertyName
261: * @return bool
262: */
263: public function __isset($sPropertyName)
264: {
265: return $this->IsProperty($sPropertyName);
266: }
267:
268: /**
269: * @param string $sKey
270: * @param mixed $mValue
271: * @return void
272: */
273: public function __set($sKey, $mValue)
274: {
275: $aMap = $this->getMap();
276: if (isset($aMap[$sKey])) {
277: $this->setType($mValue, $aMap[$sKey][0]);
278:
279: if (\key_exists($sKey, $this->aContainer)) {
280: $this->aObsolete[$sKey] = $this->aContainer[$sKey];
281: }
282:
283: if (($this->__USE_TRIM_IN_STRINGS__ && 0 === \strpos($aMap[$sKey][0], 'string')) ||
284: (\in_array($sKey, $this->aTrimer) && \is_string($mValue))) {
285: $mValue = trim($mValue);
286: }
287:
288: if (\is_string($mValue)) {
289: if (\in_array($sKey, $this->aLower)) {
290: $mValue = \strtolower($mValue);
291: } elseif (\in_array($sKey, $this->aUpper)) {
292: $mValue = \strtoupper($mValue);
293: }
294: }
295:
296: $this->aContainer[$sKey] = $mValue;
297: } else {
298: throw new Exceptions\BaseException(Exceptions\Errs::Container_UndefinedProperty, null, array('{{PropertyName}}' => $sKey));
299: }
300: }
301:
302: /**
303: * @param string $sKey
304: *
305: * @throws Exception
306: *
307: * @return mixed
308: */
309: public function __get($sKey)
310: {
311: $mReturn = null;
312: if (\array_key_exists($sKey, $this->aContainer)) {
313: $mReturn = $this->aContainer[$sKey];
314: } else {
315: throw new Exceptions\Exception('Undefined property '.$sKey);
316: }
317:
318: return $mReturn;
319: }
320:
321: /**
322: * @param mixed $mValue
323: * @param string $sType
324: */
325: protected function setType(&$mValue, $sType)
326: {
327: $sType = \strtolower($sType);
328: if (\in_array($sType, array('string', 'int', 'bool', 'array'))) {
329: \settype($mValue, $sType);
330: } elseif (\in_array($sType, array('datetime'))) {
331: \settype($mValue, 'int');
332: } elseif (\in_array($sType, array('password'))) {
333: \settype($mValue, 'string');
334: } elseif (0 === \strpos($sType, 'string(')) {
335: \settype($mValue, 'string');
336: if (0 < \strlen($mValue)) {
337: $iSize = \substr($sType, 7, -1);
338: if (\is_numeric($iSize) && (int) $iSize < \strlen($mValue)) {
339: // $mValue = substr($mValue, 0, (int) $iSize);
340: $mValue = Utils::Utf8Truncate($mValue, (int) $iSize);
341: }
342: }
343: }
344: }
345:
346: /**
347: * @return bool
348: */
349: public function initBeforeChange()
350: {
351: return true;
352: }
353:
354: /**
355: * @return bool
356: */
357: public function validate()
358: {
359: return true;
360: }
361:
362: /**
363: * @return array
364: */
365: public function getMap()
366: {
367: return array();
368: }
369:
370: /**
371: * @return array
372: */
373: public static function getStaticMap()
374: {
375: return array();
376: }
377:
378: /**
379: * @param array $aMap
380: *
381: * @return array
382: */
383: public static function DbReadKeys($aMap)
384: {
385: $aResult = array();
386: foreach ($aMap as $aTypes) {
387: if (isset($aTypes[1])) {
388: $aResult[] = $aTypes[1];
389: }
390: }
391: return $aResult;
392: }
393:
394: /**
395: * @param array $aMap
396: * @param bool $bInsert
397: *
398: * @return array
399: */
400: public static function DbWriteKeys($aMap, $bInsert)
401: {
402: $aResult = array();
403: foreach ($aMap as $sKey => $aTypes) {
404: if (isset($aTypes[1])) {
405: $bUseInInsert = $bUseInUpdate = true;
406:
407: if (isset($aTypes[2]) && !$aTypes[2]) {
408: $bUseInInsert = false;
409: }
410:
411: if (isset($aTypes[3]) && !$aTypes[3]) {
412: $bUseInUpdate = false;
413: }
414:
415: if (($bInsert && $bUseInInsert) || (!$bInsert && $bUseInUpdate)) {
416: $aResult[$aTypes[1]] = $sKey;
417: }
418: }
419: }
420: return $aResult;
421: }
422:
423: /**
424: * @param object $oObject
425: * @param object $oHelper
426: * @param array $aExclude
427: *
428: * @return array
429: */
430: public static function DbUpdateArray($oObject, $oHelper, $aExclude = array())
431: {
432: $aResult = array();
433: $aExclude = \is_array($aExclude) && 0 < \count($aExclude) ? $aExclude : array();
434:
435: $sQueryParams = '';
436: $oSettings =& Api::GetSettings();
437: $bUseLogQueryParams = (bool) $oSettings->GetValue('DBLogQueryParams', false);
438:
439: $oObject->initBeforeChange();
440:
441: $aStaticMap = $oObject->getMap();
442: $aMap = AbstractContainer::DbWriteKeys($aStaticMap, false);
443:
444: foreach ($aMap as $sDbKey => $sObjectKey) {
445: if (\in_array($sDbKey, $aExclude)) {
446: continue;
447: }
448:
449: $mValue = $oObject->{$sObjectKey};
450: if (isset($aStaticMap[$sObjectKey][0])) {
451: if ('password' === $aStaticMap[$sObjectKey][0]) {
452: $mValue = Utils::EncryptValue($mValue);
453: } elseif ('datetime' === $aStaticMap[$sObjectKey][0]) {
454: $mValue = $oHelper->TimeStampToDateFormat($mValue);
455: } elseif ('serialize' === $aStaticMap[$sObjectKey][0]) {
456: $mValue = '' === $mValue ? '' : \serialize($mValue);
457: }
458: }
459:
460: $aResult[] = $oHelper->EscapeColumn($sDbKey).' = '.
461: (\is_string($mValue) ? $oHelper->EscapeString($mValue) : (int) $mValue);
462:
463: if ($bUseLogQueryParams) {
464: $sQueryParams .=
465: AU_API_CRLF.AU_API_TAB.$sDbKey.' = '.(
466: \is_string($mValue) ? $oHelper->EscapeString($mValue) : (int) $mValue
467: );
468: }
469: }
470:
471: if ($bUseLogQueryParams) {
472: Api::Log($sQueryParams);
473: }
474:
475: return $aResult;
476: }
477:
478: /**
479: * @param string $sWhere
480: * @param string $sTableName
481: * @param array $aStaticMap
482: * @param object $oHelper
483: *
484: * @return string
485: */
486: public static function DbGetObjectSqlString($sWhere, $sTableName, $aStaticMap, $oHelper)
487: {
488: $aMap = AbstractContainer::DbReadKeys($aStaticMap);
489: $aMap = \array_map(array($oHelper, 'EscapeColumn'), $aMap);
490:
491: $sSql = 'SELECT %s FROM %s WHERE %s';
492:
493: return \sprintf($sSql, \implode(', ', $aMap), $sTableName, $sWhere);
494: }
495:
496: /**
497: * @param string $sTableName
498: * @param object $oObject
499: * @param object $oHelper
500: *
501: * @return string
502: */
503: public static function DbCreateObjectSqlString($sTableName, $oObject, $oHelper)
504: {
505: $sSql = '';
506: $aResults = self::DbInsertArrays($oObject, $oHelper);
507:
508: if ($aResults[0] && $aResults[1]) {
509: $sSql = 'INSERT INTO %s ( %s ) VALUES ( %s )';
510: $sSql = \sprintf($sSql, $sTableName, \implode(', ', $aResults[0]), \implode(', ', $aResults[1]));
511: }
512:
513: return $sSql;
514: }
515:
516: /**
517: * @param string $sTableName
518: * @param object $oObject
519: * @param object $oHelper
520: *
521: * @return string
522: */
523: public static function DbUpdateObjectSqlString($sTableName, $oObject, $oHelper)
524: {
525: $aResult = self::DbUpdateArray($oObject, $oHelper);
526: $mValue = $oObject->SessionUniquePropertyValue();
527:
528: $sSql = 'UPDATE %s SET %s WHERE %s = %s';
529: return \sprintf(
530: $sSql,
531: $sTableName,
532: \implode(', ', $aResult),
533: $oHelper->EscapeColumn($oObject->SessionUniqueProperty()),
534: (\is_string($mValue) ? $oHelper->EscapeString($mValue) : (int) $mValue)
535: );
536: }
537:
538: /**
539: * @param object $oObject
540: * @param object $oHelper
541: * @return array
542: */
543: public static function DbInsertArrays($oObject, $oHelper)
544: {
545: $aResult = array(false, false);
546:
547: $sQueryParams = '';
548: $oSettings =& Api::GetSettings();
549: $bUseLogQueryParams = (bool) $oSettings->GetValue('DBLogQueryParams', false);
550:
551: $oObject->initBeforeChange();
552:
553: $aStaticMap = $oObject->getMap();
554: $aMap = AbstractContainer::DbWriteKeys($aStaticMap, true);
555:
556: $aDbKeys = \array_keys($aMap);
557: $aResult[0] = \array_map(array(&$oHelper, 'EscapeColumn'), $aDbKeys);
558:
559: $aDbValues = \array_values($aMap);
560: foreach ($aDbValues as $iIndex => $sKey) {
561: $mValue = $oObject->{$sKey};
562: if (isset($aStaticMap[$sKey][0])) {
563: if ('password' === $aStaticMap[$sKey][0]) {
564: $mValue = Utils::EncryptValue($mValue);
565: } elseif ('datetime' === $aStaticMap[$sKey][0]) {
566: $mValue = $oHelper->TimeStampToDateFormat($mValue);
567: } elseif ('serialize' === $aStaticMap[$sKey][0]) {
568: $mValue = '' === $mValue ? '' : serialize($mValue);
569: }
570: }
571:
572: $aDbValues[$iIndex] = \is_string($mValue)
573: ? $oHelper->EscapeString($mValue) : (int) $mValue;
574:
575: if ($bUseLogQueryParams) {
576: $sDbKey = isset($aDbKeys[$iIndex]) ? $aDbKeys[$iIndex] : '!unknown!';
577: $sQueryParams .= AU_API_CRLF.AU_API_TAB.$sDbKey.' = '.$aDbValues[$iIndex];
578: }
579: }
580:
581: $aResult[1] = $aDbValues;
582:
583: if ($bUseLogQueryParams) {
584: Api::Log($sQueryParams);
585: }
586:
587: return $aResult;
588: }
589: }
590: