wikimedia/mediawiki-core

View on GitHub
includes/libs/rdbms/expression/LikeValue.php

Summary

Maintainability
A
25 mins
Test Coverage
<?php

namespace Wikimedia\Rdbms;

use InvalidArgumentException;
use Wikimedia\Rdbms\Database\DbQuoter;

/**
 * Content of like value
 *
 * @newable
 * @since 1.42
 */
class LikeValue {
    /** @var (string|LikeMatch)[] */
    private array $values = [];

    /**
     * @param string|LikeMatch $value
     * @param string|LikeMatch ...$values
     */
    public function __construct( $value, ...$values ) {
        if ( !is_string( $value ) && !( $value instanceof LikeMatch ) ) {
            $type = get_debug_type( $value );
            throw new InvalidArgumentException( "\$value must be string or LikeMatch, got $type" );
        }
        $this->values = [ $value ];

        foreach ( $values as $value ) {
            if ( !is_string( $value ) && !( $value instanceof LikeMatch ) ) {
                $type = get_debug_type( $value );
                throw new InvalidArgumentException( "\$value must be string or LikeMatch, got $type" );
            }
            $this->values[] = $value;
        }
    }

    /**
     * @internal to be used by rdbms library only
     * @return-taint none
     */
    public function toSql( DbQuoter $dbQuoter ): string {
        $s = '';

        // We use ` instead of \ as the default LIKE escape character, since addQuotes()
        // may escape backslashes, creating problems of double escaping. The `
        // character has good cross-DBMS compatibility, avoiding special operators
        // in MS SQL like ^ and %
        $escapeChar = '`';

        foreach ( $this->values as $value ) {
            if ( $value instanceof LikeMatch ) {
                $s .= $value->toString();
            } else {
                $s .= $this->escapeLikeInternal( $value, $escapeChar );
            }
        }

        return $dbQuoter->addQuotes( $s ) . ' ESCAPE ' . $dbQuoter->addQuotes( $escapeChar );
    }

    private function escapeLikeInternal( $s, $escapeChar = '`' ) {
        return str_replace(
            [ $escapeChar, '%', '_' ],
            [ "{$escapeChar}{$escapeChar}", "{$escapeChar}%", "{$escapeChar}_" ],
            $s
        );
    }
}