1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
expressionfield.php
См. документацию.
1<?php
8
9namespace Bitrix\Main\ORM\Fields;
10
11use Bitrix\Main\DB\SqlExpression;
12use Bitrix\Main\ORM\Entity;
13use Bitrix\Main\ORM\Query\Chain;
14use Bitrix\Main\ORM\Data\Result;
15use Bitrix\Main\NotImplementedException;
16use Bitrix\Main\SystemException;
17
24class ExpressionField extends Field implements IReadable
25{
29 protected $expression;
30
35 protected $fullExpression;
36
38 protected $valueType;
39
43 protected $valueField;
44
48 protected $buildFrom;
49
52
53 protected $isAggregated;
54
55 protected $hasSubquery;
56
57 protected static
58 $aggrFunctionsMYSQL = array('AVG', 'BIT_AND', 'BIT_OR', 'BIT_XOR', 'COUNT',
59 'GROUP_CONCAT', 'MAX', 'MIN', 'STD', 'STDDEV_POP', 'STDDEV_SAMP',
60 'STDDEV', 'SUM', 'VAR_POP', 'VAR_SAMP', 'VARIANCE', 'ANY_VALUE'
61 ),
62 $aggrFunctionsMSSQL = array('AVG', 'MIN', 'CHECKSUM_AGG', 'OVER', 'COUNT',
63 'ROWCOUNT_BIG', 'COUNT_BIG', 'STDEV', 'GROUPING', 'STDEVP',
64 'GROUPING_ID', 'SUM', 'MAX', 'VAR', 'VARP'
65 ),
66 $aggrFunctionsORACLE = array('AVG', 'COLLECT', 'CORR', 'CORR_S', 'CORR_K',
67 'COUNT', 'COVAR_POP', 'COVAR_SAMP', 'CUME_DIST', 'DENSE_RANK', 'FIRST',
68 'GROUP_ID', 'GROUPING', 'GROUPING_ID', 'LAST', 'MAX', 'MEDIAN', 'MIN',
69 'PERCENTILE_CONT', 'PERCENTILE_DISC', 'PERCENT_RANK', 'RANK',
70 'REGR_SLOPE', 'REGR_INTERCEPT', 'REGR_COUNT', 'REGR_R2', 'REGR_AVGX',
71 'REGR_AVGY', 'REGR_SXX', 'REGR_SYY', 'REGR_SXY', 'STATS_BINOMIAL_TEST',
72 'STATS_CROSSTAB', 'STATS_F_TEST', 'STATS_KS_TEST', 'STATS_MODE',
73 'STATS_MW_TEST', 'STATS_ONE_WAY_ANOVA', 'STATS_T_TEST_ONE',
74 'STATS_T_TEST_PAIRED', 'STATS_T_TEST_INDEP', 'STATS_T_TEST_INDEPU',
75 'STATS_WSR_TEST', 'STDDEV', 'STDDEV_POP', 'STDDEV_SAMP', 'SUM',
76 'VAR_POP', 'VAR_SAMP', 'VARIANCE'
77 ),
79 'AVG', 'ARRAY_AGG', 'BOOL_AND', 'BOOL_OR', 'COUNT', 'STRING_AGG', 'SUM',
80 'MAX', 'MIN'
81 ),
83
95 public function __construct($name, $expression, $buildFrom = null, $parameters = array())
96 {
97 if (!isset($parameters['data_type']))
98 {
99 $parameters['data_type'] = 'string'; // deprecated
100
101 $this->valueType = StringField::class;
102 }
103
104 parent::__construct($name, $parameters);
105
106 $this->expression = $expression;
107
108 if (!is_array($buildFrom) && $buildFrom !== null)
109 {
111 }
112 elseif ($buildFrom === null)
113 {
114 $buildFrom = array();
115 }
116
117 $this->buildFrom = $buildFrom;
118 }
119
120 public function __call($name, $arguments)
121 {
122 return call_user_func_array(array($this->valueField, $name), $arguments);
123 }
124
128 public function getTypeMask()
129 {
131 }
132
138 public function configureValueType($class)
139 {
140 $this->valueType = $class;
141 return $this;
142 }
143
148 public function configureValueField($field)
149 {
150 $this->valueField = $field;
151 $this->valueType = get_class($field);
152
153 return $this;
154 }
155
162 public function setEntity(Entity $entity)
163 {
164 parent::setEntity($entity);
165
166 $parameters = $this->initialParameters;
167 unset($parameters['expression']);
168
169 if ($this->valueType !== null)
170 {
171 if ($this->valueField === null)
172 {
174 $valueField = new $this->valueType($this->name, $parameters);
175 $this->valueField = $this->entity->initializeField($this->name, $valueField);
176 }
177 }
178 else
179 {
180 // deprecated - old format with parameters and data_type
181 $this->valueField = $this->entity->initializeField($this->name, $parameters);
182 $this->valueType = get_class($this->valueField);
183 }
184
185 if (!($this->valueField instanceof ScalarField))
186 {
187 throw new SystemException('expression field can only be a scalar type.');
188 }
189 }
190
191 public function getExpression()
192 {
193 return $this->expression;
194 }
195
199 public function getBuildFrom()
200 {
201 return $this->buildFrom;
202 }
203
208 public function getFullExpression()
209 {
210 if (!isset($this->fullExpression))
211 {
212 $SQLBuildFrom = array();
214
215 foreach ($this->buildFrom as $element)
216 {
217 if ($element instanceof \Closure)
218 {
220 // no need to get real value. also it may [] to false positive check in hasAggregation or hasSubquery
221 //$sqlExpression = $element();
222 //$SQLBuildFrom[] = $sqlExpression->compile();
223 $SQLBuildFrom[] = '';
224 }
225 else
226 {
227 $chain = array_shift($buildFromChains);
228
229 if ($chain->getLastElement()->getValue() instanceof ExpressionField)
230 {
231 $SQLBuildFrom[] = $chain->getLastElement()->getValue()->getFullExpression();
232 }
233 else
234 {
235 $SQLBuildFrom[] = '%s';
236 }
237 }
238 }
239
240 $this->fullExpression = call_user_func_array('sprintf', array_merge(array($this->expression), $SQLBuildFrom));
241 }
242
244 }
245
250 public function isAggregated()
251 {
252 if (!isset($this->isAggregated))
253 {
254 $this->isAggregated = (bool) self::checkAggregation($this->getFullExpression());
255 }
256
257 return $this->isAggregated;
258 }
259
264 public function hasSubquery()
265 {
266 if (!isset($this->hasSubquery))
267 {
268 $this->hasSubquery = (bool) self::checkSubquery($this->getFullExpression());
269 }
270
271 return $this->hasSubquery;
272 }
273
274 public function isConstant()
275 {
276 return empty($this->buildFrom);
277 }
278
284 public function getBuildFromChains()
285 {
286 if (is_null($this->buildFromChains))
287 {
288 $this->buildFromChains = array();
289
290 foreach ($this->buildFrom as $elem)
291 {
292 if (!($elem instanceof \Closure))
293 {
294 // validate if build from scalar or expression
295 $chain = Chain::getChainByDefinition($this->entity, $elem);
296 $field = $chain->getLastElement()->getValue();
297
298 if ($field instanceof ScalarField || $field instanceof ExpressionField)
299 {
300 $this->buildFromChains[] = $chain;
301 }
302 else
303 {
304 throw new SystemException(sprintf(
305 'Expected ScalarField or ExpressionField in `%s` build_from, but `%s` was given.',
306 $this->name, is_object($field) ? get_class($field).':'.$field->getName() : gettype($field)
307 ));
308 }
309 }
310 }
311 }
312
314 }
315
316 public static function checkAggregation($expression)
317 {
318 if (empty(self::$aggrFunctions))
319 {
320 self::$aggrFunctions = array_unique(array_merge(
321 self::$aggrFunctionsMYSQL, self::$aggrFunctionsMSSQL, self::$aggrFunctionsORACLE, self::$aggrFunctionsPGSQL
322 ));
323 }
324
325 // should remove subqueries from expression here: EXISTS(..(..)..), (SELECT ..(..)..)
326 $expression = static::removeSubqueries($expression);
327
328 // then check for aggr functions
329 preg_match_all('/(?:^|[^a-z0-9_])('.join('|', self::$aggrFunctions).')[\s\‍(]+/i', $expression, $matches);
330
331 return $matches[1] ?? null;
332 }
333
334 public static function checkSubquery($expression)
335 {
336 return (preg_match('/(?:^|[^a-zA-Z0-9_])EXISTS\s*\‍(/i', $expression) || preg_match('/(?:^|[^a-zA_Z0-9_])\‍(\s*SELECT/i', $expression));
337 }
338
339 public static function removeSubqueries($expression)
340 {
341 // remove double slashes
342 $expression = str_replace('\\\\\\\\', '', $expression);
343
344 // remove strings
345 $expression = static::removeStrings('"', $expression);
346 $expression = static::removeStrings("'", $expression);
347
348 // remove subqueries' bodies
349 $clear = static::removeSubqueryBody($expression);
350
351 while ($clear !== $expression)
352 {
353 $expression = $clear;
354 $clear = static::removeSubqueryBody($expression);
355 }
356
357 return $clear;
358 }
359
360 protected static function removeStrings($quote, $expression)
361 {
362 // remove escaped quotes
363 $expression = str_replace('\\' . $quote, '', $expression);
364
365 // remove quoted strings
366 $expression = preg_replace('/' . $quote . '.*?' . $quote . '/', '', $expression);
367
368 return $expression;
369 }
370
371 protected static function removeSubqueryBody($query)
372 {
373 $subqPattern = '\‍(\s*SELECT\s+';
374
375 $matches = null;
376 preg_match('/' . $subqPattern . '/i', $query, $matches);
377
378 if (!empty($matches))
379 {
380 $substring = $matches[0];
381
382 $subqPosition = mb_strpos($query, $substring);
383 $subqStartPosition = $subqPosition + mb_strlen($substring);
384
385 $bracketsCount = 1;
386 $currentPosition = $subqStartPosition;
387
388 // until initial bracket is closed
389 while ($bracketsCount > 0)
390 {
391 $symbol = mb_substr($query, $currentPosition, 1);
392
393 if ($symbol == '')
394 {
395 // end of string
396 break;
397 }
398
399 if ($symbol == '(')
400 {
401 $bracketsCount++;
402 }
403 elseif ($symbol == ')')
404 {
405 $bracketsCount--;
406 }
407
408 $currentPosition++;
409 }
410
411 $query = mb_substr($query, 0, $subqPosition).mb_substr($query, $currentPosition);
412 }
413
414 return $query;
415 }
416
421 public function getDataType()
422 {
423 return $this->valueField->getDataType();
424 }
425
429 public function getValueType()
430 {
431 return $this->valueType;
432 }
433
437 public function getValueField()
438 {
439 return $this->valueField;
440 }
441
442 public function __clone()
443 {
444 $this->buildFromChains = null;
445 $this->fullExpression = null;
446 }
447
453 public function cast($value)
454 {
456 return $valueField->cast($value);
457 }
458
464 public function convertValueFromDb($value)
465 {
467 return $valueField->convertValueFromDb($value);
468 }
469
475 public function convertValueToDb($value)
476 {
479 return $valueField->convertValueToDb($value);
480 }
481
482 public function validateValue($value, $primary, $row, Result $result)
483 {
484 throw new NotImplementedException;
485 }
486}
487
488
__construct($name, $expression, $buildFrom=null, $parameters=array())
Определения expressionfield.php:95
static removeStrings($quote, $expression)
Определения expressionfield.php:360
__call($name, $arguments)
Определения expressionfield.php:120
static removeSubqueries($expression)
Определения expressionfield.php:339
static checkAggregation($expression)
Определения expressionfield.php:316
validateValue($value, $primary, $row, Result $result)
Определения expressionfield.php:482
static removeSubqueryBody($query)
Определения expressionfield.php:371
static checkSubquery($expression)
Определения expressionfield.php:334
setEntity(Entity $entity)
Определения field.php:151
$initialParameters
Определения field.php:33
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$result
Определения get_property_values.php:14
$query
Определения get_search.php:11
$entity
Определения ufield.php:9
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$matches
Определения index.php:22