Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
21.74% covered (danger)
21.74%
5 / 23
40.00% covered (danger)
40.00%
2 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
ExpressionGroup
21.74% covered (danger)
21.74%
5 / 23
40.00% covered (danger)
40.00%
2 / 5
81.02
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 add
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getType
n/a
0 / 0
n/a
0 / 0
0
 newFromArray
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
42
 toSql
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 toGeneralizedSql
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace Wikimedia\Rdbms;
4
5use InvalidArgumentException;
6use Wikimedia\Rdbms\Database\DbQuoter;
7
8/**
9 * A composite node representing a group of expressions.
10 *
11 * @since 1.42
12 */
13abstract class ExpressionGroup implements IExpression {
14    /**
15     * @var IExpression[]
16     */
17    protected array $children = [];
18
19    /**
20     * @param IExpression ...$children
21     * @internal Outside of rdbms, Use IReadableDatabase::andExpr() or ::orExpr to create an expression group object
22     */
23    public function __construct( IExpression ...$children ) {
24        $this->children = $children;
25    }
26
27    final protected function add( IExpression $expression ) {
28        $this->children[] = $expression;
29    }
30
31    abstract protected function getType(): string;
32
33    /**
34     * @internal to rdbms
35     * @phpcs:ignore Generic.Files.LineLength
36     * @param non-empty-array<string,?scalar|RawSQLValue|Blob|LikeValue|non-empty-list<scalar|Blob>>|non-empty-array<int,IExpression> $conds
37     * @param-taint $conds exec_sql_numkey
38     */
39    public static function newFromArray( array $conds ): static {
40        if ( !$conds ) {
41            throw new InvalidArgumentException( "The array of conditions can't be empty." );
42        }
43        $exprs = [];
44        foreach ( $conds as $field => $cond ) {
45            if ( is_numeric( $field ) ) {
46                if ( !$cond instanceof IExpression ) {
47                    throw new InvalidArgumentException(
48                        __METHOD__ . ": Only IExpression are allowed with numeric key." );
49                }
50                $exprs[] = $cond;
51            } else {
52                if ( $cond instanceof IExpression ) {
53                    throw new InvalidArgumentException( __METHOD__ . ": unexpected key $field for IExpression value" );
54                }
55                $exprs[] = new Expression( $field, '=', $cond );
56            }
57        }
58        // @phan-suppress-next-line PhanTypeInstantiateAbstractStatic
59        return new static( ...$exprs );
60    }
61
62    /**
63     * @param DbQuoter $dbQuoter
64     * @return string
65     * @return-taint none
66     */
67    final public function toSql( DbQuoter $dbQuoter ): string {
68        if ( !$this->children ) {
69            throw new InvalidArgumentException( "The array of values can't be empty." );
70        }
71        $sqls = array_map( static fn ( $value ) => $value->toSql( $dbQuoter ), $this->children );
72        return '(' . implode( ' ' . $this->getType() . ' ', $sqls ) . ')';
73    }
74
75    final public function toGeneralizedSql(): string {
76        if ( !$this->children ) {
77            throw new InvalidArgumentException( "The array of values can't be empty." );
78        }
79        $sqls = array_map( static fn ( $value ) => $value->toGeneralizedSql(), $this->children );
80        return '(' . implode( ' ' . $this->getType() . ' ', $sqls ) . ')';
81    }
82}