-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4039 from morozov/refactor-portability-statement
Refactor portability statement into a functional composition
- Loading branch information
Showing
5 changed files
with
812 additions
and
172 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,247 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Doctrine\DBAL\Portability; | ||
|
||
use function array_change_key_case; | ||
use function array_map; | ||
use function array_reduce; | ||
use function is_string; | ||
use function rtrim; | ||
|
||
final class Converter | ||
{ | ||
/** @var callable */ | ||
private $convertNumeric; | ||
|
||
/** @var callable */ | ||
private $convertAssociative; | ||
|
||
/** @var callable */ | ||
private $convertOne; | ||
|
||
/** @var callable */ | ||
private $convertAllNumeric; | ||
|
||
/** @var callable */ | ||
private $convertAllAssociative; | ||
|
||
/** @var callable */ | ||
private $convertFirstColumn; | ||
|
||
/** | ||
* @param bool $convertEmptyStringToNull Whether each empty string should be converted to NULL | ||
* @param bool $rightTrimString Whether each string should right-trimmed | ||
* @param int|null $case Convert the case of the column names | ||
* (one of {@link CASE_LOWER} and {@link CASE_UPPER}) | ||
*/ | ||
public function __construct(bool $convertEmptyStringToNull, bool $rightTrimString, ?int $case) | ||
{ | ||
$id = static function ($value) { | ||
return $value; | ||
}; | ||
|
||
$convertValue = $this->createConvertValue($convertEmptyStringToNull, $rightTrimString); | ||
$convertNumeric = $this->createConvertRow($convertValue, null); | ||
$convertAssociative = $this->createConvertRow($convertValue, $case); | ||
|
||
$this->convertNumeric = $this->createConvert($convertNumeric, $id); | ||
$this->convertAssociative = $this->createConvert($convertAssociative, $id); | ||
$this->convertOne = $this->createConvert($convertValue, $id); | ||
|
||
$this->convertAllNumeric = $this->createConvertAll($convertNumeric, $id); | ||
$this->convertAllAssociative = $this->createConvertAll($convertAssociative, $id); | ||
$this->convertFirstColumn = $this->createConvertAll($convertValue, $id); | ||
} | ||
|
||
/** | ||
* @param array<int,mixed>|false $row | ||
* | ||
* @return array<int,mixed>|false | ||
*/ | ||
public function convertNumeric($row) | ||
{ | ||
return ($this->convertNumeric)($row); | ||
} | ||
|
||
/** | ||
* @param array<string,mixed>|false $row | ||
* | ||
* @return array<string,mixed>|false | ||
*/ | ||
public function convertAssociative($row) | ||
{ | ||
return ($this->convertAssociative)($row); | ||
} | ||
|
||
/** | ||
* @param mixed|false $value | ||
* | ||
* @return mixed|false | ||
*/ | ||
public function convertOne($value) | ||
{ | ||
return ($this->convertOne)($value); | ||
} | ||
|
||
/** | ||
* @param array<int,array<int,mixed>> $data | ||
* | ||
* @return array<int,array<int,mixed>> | ||
*/ | ||
public function convertAllNumeric(array $data) : array | ||
{ | ||
return ($this->convertAllNumeric)($data); | ||
} | ||
|
||
/** | ||
* @param array<int,array<string,mixed>> $data | ||
* | ||
* @return array<int,array<string,mixed>> | ||
*/ | ||
public function convertAllAssociative(array $data) : array | ||
{ | ||
return ($this->convertAllAssociative)($data); | ||
} | ||
|
||
/** | ||
* @param array<int,mixed> $data | ||
* | ||
* @return array<int,mixed> | ||
*/ | ||
public function convertFirstColumn(array $data) : array | ||
{ | ||
return ($this->convertFirstColumn)($data); | ||
} | ||
|
||
/** | ||
* Creates a function that will convert each individual value retrieved from the database | ||
* | ||
* @param bool $convertEmptyStringToNull Whether each empty string should be converted to NULL | ||
* @param bool $rightTrimString Whether each string should right-trimmed | ||
* | ||
* @return callable|null The resulting function or NULL if no conversion is needed | ||
*/ | ||
private function createConvertValue(bool $convertEmptyStringToNull, bool $rightTrimString) : ?callable | ||
{ | ||
$functions = []; | ||
|
||
if ($convertEmptyStringToNull) { | ||
$functions[] = static function ($value) { | ||
if ($value === '') { | ||
return null; | ||
} | ||
|
||
return $value; | ||
}; | ||
} | ||
|
||
if ($rightTrimString) { | ||
$functions[] = static function ($value) { | ||
if (! is_string($value)) { | ||
return $value; | ||
} | ||
|
||
return rtrim($value); | ||
}; | ||
} | ||
|
||
return $this->compose(...$functions); | ||
} | ||
|
||
/** | ||
* Creates a function that will convert each array-row retrieved from the database | ||
* | ||
* @param callable|null $function The function that will convert each value | ||
* @param int|null $case Column name case | ||
* | ||
* @return callable|null The resulting function or NULL if no conversion is needed | ||
*/ | ||
private function createConvertRow(?callable $function, ?int $case) : ?callable | ||
{ | ||
$functions = []; | ||
|
||
if ($function !== null) { | ||
$functions[] = $this->createMapper($function); | ||
} | ||
|
||
if ($case !== null) { | ||
$functions[] = static function (array $row) use ($case) : array { | ||
return array_change_key_case($row, $case); | ||
}; | ||
} | ||
|
||
return $this->compose(...$functions); | ||
} | ||
|
||
/** | ||
* Creates a function that will be applied to the return value of Statement::fetch*() | ||
* or an identity function if no conversion is needed | ||
* | ||
* @param callable|null $function The function that will convert each tow | ||
* @param callable $id Identity function | ||
*/ | ||
private function createConvert(?callable $function, callable $id) : callable | ||
{ | ||
if ($function === null) { | ||
return $id; | ||
} | ||
|
||
return static function ($value) use ($function) { | ||
if ($value === false) { | ||
return false; | ||
} | ||
|
||
return $function($value); | ||
}; | ||
} | ||
|
||
/** | ||
* Creates a function that will be applied to the return value of Statement::fetchAll*() | ||
* or an identity function if no transformation is required | ||
* | ||
* @param callable|null $function The function that will transform each value | ||
* @param callable $id Identity function | ||
*/ | ||
private function createConvertAll(?callable $function, callable $id) : callable | ||
{ | ||
if ($function === null) { | ||
return $id; | ||
} | ||
|
||
return $this->createMapper($function); | ||
} | ||
|
||
/** | ||
* Creates a function that maps each value of the array using the given function | ||
* | ||
* @param callable $function The function that maps each value of the array | ||
*/ | ||
private function createMapper(callable $function) : callable | ||
{ | ||
return static function (array $array) use ($function) : array { | ||
return array_map($function, $array); | ||
}; | ||
} | ||
|
||
/** | ||
* Creates a composition of the given set of functions | ||
* | ||
* @param callable ...$functions The functions to compose | ||
* | ||
* @return callable|null The composition or NULL if an empty set is provided | ||
*/ | ||
private function compose(callable ...$functions) : ?callable | ||
{ | ||
return array_reduce($functions, static function (?callable $carry, callable $item) : callable { | ||
if ($carry === null) { | ||
return $item; | ||
} | ||
|
||
return static function ($value) use ($carry, $item) { | ||
return $item($carry($value)); | ||
}; | ||
}); | ||
} | ||
} |
Oops, something went wrong.