text = $text; $this->args = self::flat( $args ); } public static function of( string $text, ...$args ): self { return new self( $text, ...$args ); } public function add( string|SqlPart $text, ...$args ): self { if ( $text instanceof self ) { return new self( $this->text . $text->text, ...array_merge( $this->args, $text->args ) ); } return new self( $this->text . $text, ...array_merge( $this->args, self::flat( $args ) ) ); } public static function join( string $glue, array $parts ): self { $text = array(); $args = array(); foreach ( $parts as $part ) { $part = is_string( $part ) ? new self( $part ) : $part; if ( ! $part instanceof self || '' === $part->text ) { continue; } $text[] = $part->text; $args = array_merge( $args, $part->args ); } return new self( implode( $glue, $text ), ...$args ); } public static function and_in( string $column, array $ids ): self { $ids = array_values( array_map( 'intval', $ids ) ); if ( array() === $ids ) { return new self(); } $parts = explode( '.', $column, 2 ); $ident = 2 === count( $parts ) ? '%i.%i' : '%i'; $marks = 1 === count( $ids ) ? ' = %d' : ' IN (' . implode( ',', array_fill( 0, count( $ids ), '%d' ) ) . ')'; return new self( ' AND ' . $ident . $marks, ...array_merge( $parts, $ids ) ); } public static function in_list( array $values, string $mark = '%d' ): self { $values = array_values( $values ); $mark = '%s' === $mark ? '%s' : '%d'; return new self( implode( ',', array_fill( 0, count( $values ), $mark ) ), ...$values ); } public function strip_prefix( string $prefix ): self { return '' !== $prefix && str_starts_with( $this->text, $prefix ) ? new self( substr( $this->text, strlen( $prefix ) ), ...$this->args ) : $this; } public function text(): string { return $this->text; } public function args(): array { return $this->args; } public function is_empty(): bool { return '' === $this->text; } private static function flat( array $args ): array { return 1 === count( $args ) && is_array( $args[0] ) ? array_values( $args[0] ) : array_values( $args ); } }