diff --git a/src/Illuminate/Database/Eloquent/Builder.php b/src/Illuminate/Database/Eloquent/Builder.php index 8ab8e055e77f..058a7a528f8d 100755 --- a/src/Illuminate/Database/Eloquent/Builder.php +++ b/src/Illuminate/Database/Eloquent/Builder.php @@ -89,6 +89,9 @@ class Builder implements BuilderContract /** * The methods that should be returned from query builder. * + * Note that the names in the array need to be lowercase so that PHP's case-insensitivity + * regarding method calls can be achieved when forwarding the call to the base object. + * * @var string[] */ protected $passthru = [ @@ -97,29 +100,29 @@ class Builder implements BuilderContract 'avg', 'count', 'dd', - 'ddRawSql', - 'doesntExist', - 'doesntExistOr', + 'ddrawsql', + 'doesntexist', + 'doesntexistor', 'dump', - 'dumpRawSql', + 'dumprawsql', 'exists', - 'existsOr', + 'existsor', 'explain', - 'getBindings', - 'getConnection', - 'getGrammar', + 'getbindings', + 'getconnection', + 'getgrammar', 'implode', 'insert', - 'insertGetId', - 'insertOrIgnore', - 'insertUsing', + 'insertgetid', + 'insertorignore', + 'insertusing', 'max', 'min', 'raw', - 'rawValue', + 'rawvalue', 'sum', - 'toSql', - 'toRawSql', + 'tosql', + 'torawsql', ]; /** @@ -1964,7 +1967,7 @@ public function __call($method, $parameters) return $this->callNamedScope($method, $parameters); } - if (in_array($method, $this->passthru)) { + if (in_array(strtolower($method), $this->passthru)) { return $this->toBase()->{$method}(...$parameters); } diff --git a/tests/Database/DatabaseEloquentBuilderTest.php b/tests/Database/DatabaseEloquentBuilderTest.php index 470214f20a5c..7254939d2206 100755 --- a/tests/Database/DatabaseEloquentBuilderTest.php +++ b/tests/Database/DatabaseEloquentBuilderTest.php @@ -2246,6 +2246,48 @@ public function testToRawSql() $this->assertSame('select * from "users" where "email" = \'foo\'', $builder->toRawSql()); } + public function testPassthruMethodsCallsAreNotCaseSensitive() + { + $query = m::mock(BaseBuilder::class); + + $mockResponse = 'select 1'; + $query + ->shouldReceive('toRawSql') + ->andReturn($mockResponse) + ->times(3); + + $builder = new Builder($query); + + $this->assertSame('select 1', $builder->TORAWSQL()); + $this->assertSame('select 1', $builder->toRawSql()); + $this->assertSame('select 1', $builder->toRawSQL()); + } + + public function testPassthruArrayElementsMustAllBeLowercase() + { + $builder = new class(m::mock(BaseBuilder::class)) extends Builder + { + // expose protected member for test + public function getPassthru(): array + { + return $this->passthru; + } + }; + + $passthru = $builder->getPassthru(); + + foreach ($passthru as $method) { + $lowercaseMethod = strtolower($method); + + $this->assertSame( + $lowercaseMethod, + $method, + 'Eloquent\\Builder relies on lowercase method names in passthru array to correctly mimic PHP case-insensitivity on method dispatch.' . + 'If you are adding a new method to the \$passthru array, make sure the name is lowercased.' + ); + } + } + protected function mockConnectionForModel($model, $database) { $grammarClass = 'Illuminate\Database\Query\Grammars\\'.$database.'Grammar';