├── .github ├── CODEOWNERS.yml ├── FUNDING.yml ├── workflows │ ├── auto-request-review.yml │ └── ci.yml └── renovate.json ├── src └── MartinGeorgiev │ ├── Doctrine │ ├── DBAL │ │ └── Types │ │ │ ├── Exceptions │ │ │ ├── TypeException.php │ │ │ └── UnexpectedTypeOfTransformedPHPValue.php │ │ │ ├── SmallIntArray.php │ │ │ ├── IntegerArray.php │ │ │ ├── BigIntArray.php │ │ │ ├── JsonTransformer.php │ │ │ ├── BaseType.php │ │ │ ├── BooleanArray.php │ │ │ ├── Jsonb.php │ │ │ ├── BaseIntegerArray.php │ │ │ ├── JsonbArray.php │ │ │ ├── TextArray.php │ │ │ └── BaseArray.php │ └── ORM │ │ └── Query │ │ └── AST │ │ └── Functions │ │ ├── BaseComparisonFunction.php │ │ ├── StartsWith.php │ │ ├── JsonTypeof.php │ │ ├── Row.php │ │ ├── ToTsquery.php │ │ ├── Arr.php │ │ ├── Least.php │ │ ├── Greatest.php │ │ ├── ToJson.php │ │ ├── ToJsonb.php │ │ ├── Unaccent.php │ │ ├── ArrayAgg.php │ │ ├── JsonAgg.php │ │ ├── JsonbAgg.php │ │ ├── JsonEach.php │ │ ├── All.php │ │ ├── JsonbEach.php │ │ ├── JsonbPretty.php │ │ ├── RowToJson.php │ │ ├── Any.php │ │ ├── ArrayDimensions.php │ │ ├── ArrayToJson.php │ │ ├── Unnest.php │ │ ├── ArrayCardinality.php │ │ ├── JsonEachText.php │ │ ├── JsonbEachText.php │ │ ├── JsonObjectKeys.php │ │ ├── JsonStripNulls.php │ │ ├── SimilarTo.php │ │ ├── Ilike.php │ │ ├── JsonArrayLength.php │ │ ├── JsonbObjectKeys.php │ │ ├── JsonbStripNulls.php │ │ ├── ArrayNumberOfDimensions.php │ │ ├── JsonbArrayLength.php │ │ ├── JsonbArrayElements.php │ │ ├── NotSimilarTo.php │ │ ├── ToTsvector.php │ │ ├── ArrayCat.php │ │ ├── InArray.php │ │ ├── JsonbArrayElementsText.php │ │ ├── ArrayLength.php │ │ ├── JsonbExists.php │ │ ├── Tsmatch.php │ │ ├── ArrayAppend.php │ │ ├── ArrayPrepend.php │ │ ├── ArrayRemove.php │ │ ├── JsonObjectAgg.php │ │ ├── StrConcat.php │ │ ├── ArrayToString.php │ │ ├── JsonbObjectAgg.php │ │ ├── StringToArray.php │ │ ├── Regexp.php │ │ ├── RegexpLike.php │ │ ├── RegexpMatch.php │ │ ├── Contains.php │ │ ├── DeleteAtPath.php │ │ ├── IRegexp.php │ │ ├── SplitPart.php │ │ ├── JsonGetField.php │ │ ├── NotRegexp.php │ │ ├── Overlaps.php │ │ ├── IsContainedBy.php │ │ ├── JsonGetObject.php │ │ ├── NotIRegexp.php │ │ ├── DateExtract.php │ │ ├── JsonGetFieldAsText.php │ │ ├── JsonbSet.php │ │ ├── JsonbSetLax.php │ │ ├── RegexpReplace.php │ │ ├── JsonbInsert.php │ │ ├── JsonGetObjectAsText.php │ │ ├── TheRightExistsOnTheLeft.php │ │ ├── ArrayReplace.php │ │ ├── AllOnTheRightExistOnTheLeft.php │ │ ├── ReturnsValueForJsonValue.php │ │ ├── AnyOnTheRightExistsOnTheLeft.php │ │ ├── JsonGetFieldAsInteger.php │ │ ├── FlaggedRegexpReplace.php │ │ ├── DateOverlaps.php │ │ ├── FlaggedRegexpLike.php │ │ ├── FlaggedRegexpMatch.php │ │ ├── BaseVariadicFunction.php │ │ ├── StringAgg.php │ │ ├── BaseFunction.php │ │ └── Cast.php │ └── Utils │ ├── DoctrineOrm.php │ └── DataStructure.php ├── LICENSE └── composer.json /.github/CODEOWNERS.yml: -------------------------------------------------------------------------------- 1 | * @martin-georgiev 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [martin-georgiev] 2 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/DBAL/Types/Exceptions/TypeException.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | abstract class TypeException extends \Exception {} 13 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Utils/DoctrineOrm.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | abstract class BaseComparisonFunction extends BaseVariadicFunction 13 | { 14 | protected string $commonNodeMapping = 'ArithmeticPrimary'; 15 | } 16 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/StartsWith.php: -------------------------------------------------------------------------------- 1 | setFunctionPrototype('(STARTS_WITH(%s, %s))'); 12 | $this->addNodeMapping('StringPrimary'); 13 | $this->addNodeMapping('StringPrimary'); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.github/workflows/auto-request-review.yml: -------------------------------------------------------------------------------- 1 | name: Auto Request Review 2 | 3 | on: 4 | pull_request: 5 | types: 6 | - opened 7 | - ready_for_review 8 | - reopened 9 | 10 | jobs: 11 | auto-request-review: 12 | name: Auto Request Review 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Request reviews based on configuration 16 | uses: necojackarc/auto-request-review@v0.13.0 17 | with: 18 | token: ${{ secrets.GITHUB_TOKEN }} 19 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonTypeof.php: -------------------------------------------------------------------------------- 1 | setFunctionPrototype('json_typeof(%s)'); 18 | $this->addNodeMapping('StringPrimary'); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Row.php: -------------------------------------------------------------------------------- 1 | setFunctionPrototype('ROW(%s)'); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsquery.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class ToTsquery extends BaseVariadicFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('to_tsquery(%s)'); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Arr.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class Arr extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('ARRAY[%s]'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Least.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class Least extends BaseComparisonFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('least(%s)'); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Greatest.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class Greatest extends BaseComparisonFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('greatest(%s)'); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToJson.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class ToJson extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('to_json(%s)'); 20 | $this->addNodeMapping('NewValue'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToJsonb.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class ToJsonb extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('to_jsonb(%s)'); 20 | $this->addNodeMapping('NewValue'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Unaccent.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class Unaccent extends BaseVariadicFunction 15 | { 16 | protected string $commonNodeMapping = 'StringPrimary'; 17 | 18 | protected function customiseFunction(): void 19 | { 20 | $this->setFunctionPrototype('unaccent(%s)'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayAgg.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class ArrayAgg extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('array_agg(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonAgg.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class JsonAgg extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('json_agg(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbAgg.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class JsonbAgg extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('jsonb_agg(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonEach.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class JsonEach extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('json_each(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/All.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class All extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('ALL(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbEach.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class JsonbEach extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('jsonb_each(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPretty.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class JsonbPretty extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('jsonb_pretty(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RowToJson.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class RowToJson extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('row_to_json(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Any.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class Any extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('ANY(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayDimensions.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class ArrayDimensions extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('array_dims(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayToJson.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class ArrayToJson extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('array_to_json(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Unnest.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class Unnest extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('unnest(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayCardinality.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class ArrayCardinality extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('cardinality(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonEachText.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class JsonEachText extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('json_each_text(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbEachText.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class JsonbEachText extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('jsonb_each_text(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonObjectKeys.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class JsonObjectKeys extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('json_object_keys(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonStripNulls.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class JsonStripNulls extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('json_strip_nulls(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/SimilarTo.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class SimilarTo extends BaseFunction 15 | { 16 | protected function customiseFunction(): void 17 | { 18 | $this->setFunctionPrototype('%s similar to %s'); 19 | $this->addNodeMapping('StringPrimary'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Ilike.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class Ilike extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('%s ilike %s'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonArrayLength.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class JsonArrayLength extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('json_array_length(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbObjectKeys.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class JsonbObjectKeys extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('jsonb_object_keys(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbStripNulls.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class JsonbStripNulls extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('jsonb_strip_nulls(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayNumberOfDimensions.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class ArrayNumberOfDimensions extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('array_ndims(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbArrayLength.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class JsonbArrayLength extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('jsonb_array_length(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbArrayElements.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class JsonbArrayElements extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('jsonb_array_elements(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/NotSimilarTo.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class NotSimilarTo extends BaseFunction 15 | { 16 | protected function customiseFunction(): void 17 | { 18 | $this->setFunctionPrototype('%s not similar to %s'); 19 | $this->addNodeMapping('StringPrimary'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsvector.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class ToTsvector extends BaseVariadicFunction 16 | { 17 | protected string $commonNodeMapping = 'StringExpression'; 18 | 19 | protected function customiseFunction(): void 20 | { 21 | $this->setFunctionPrototype('to_tsvector(%s)'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayCat.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class ArrayCat extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('array_cat(%s, %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/InArray.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class InArray extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('%s = ANY(%s)'); 20 | $this->addNodeMapping('InputParameter'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbArrayElementsText.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class JsonbArrayElementsText extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('jsonb_array_elements_text(%s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayLength.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class ArrayLength extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('array_length(%s, %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('ArithmeticPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbExists.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class JsonbExists extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('jsonb_exists(%s, %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Tsmatch.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class Tsmatch extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('(%s @@ %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayAppend.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class ArrayAppend extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('array_append(%s, %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('ArithmeticPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayPrepend.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class ArrayPrepend extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('array_prepend(%s, %s)'); 20 | $this->addNodeMapping('ArithmeticPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayRemove.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class ArrayRemove extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('array_remove(%s, %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('ArithmeticPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonObjectAgg.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class JsonObjectAgg extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('json_object_agg(%s, %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/StrConcat.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class StrConcat extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('(%s || %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayToString.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class ArrayToString extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('array_to_string(%s, %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbObjectAgg.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class JsonbObjectAgg extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('jsonb_object_agg(%s, %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/StringToArray.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class StringToArray extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('string_to_array(%s, %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Regexp.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class Regexp extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('(%s ~ %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RegexpLike.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class RegexpLike extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('regexp_like(%s, %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RegexpMatch.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class RegexpMatch extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('regexp_match(%s, %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Contains.php: -------------------------------------------------------------------------------- 1 | ). 9 | * 10 | * @see https://www.postgresql.org/docs/9.4/static/functions-array.html 11 | * @since 0.1 12 | * 13 | * @author Martin Georgiev 14 | */ 15 | class Contains extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('(%s @> %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DeleteAtPath.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class DeleteAtPath extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('(%s #- %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/IRegexp.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class IRegexp extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('(%s ~* %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/SplitPart.php: -------------------------------------------------------------------------------- 1 | setFunctionPrototype('split_part(%s, %s, %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | $this->addNodeMapping('SimpleArithmeticExpression'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonGetField.php: -------------------------------------------------------------------------------- 1 | ). 9 | * 10 | * @see https://www.postgresql.org/docs/9.4/static/functions-json.html 11 | * @since 0.1 12 | * 13 | * @author Martin Georgiev 14 | */ 15 | class JsonGetField extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('(%s -> %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/NotRegexp.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class NotRegexp extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('(%s !~ %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Overlaps.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class Overlaps extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('(%s && %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/IsContainedBy.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class IsContainedBy extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('(%s <@ %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonGetObject.php: -------------------------------------------------------------------------------- 1 | ). 9 | * 10 | * @see https://www.postgresql.org/docs/9.4/static/functions-json.html 11 | * @since 0.1 12 | * 13 | * @author Martin Georgiev 14 | */ 15 | class JsonGetObject extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('(%s #> %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/NotIRegexp.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class NotIRegexp extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('(%s !~* %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateExtract.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class DateExtract extends BaseFunction 17 | { 18 | protected function customiseFunction(): void 19 | { 20 | $this->setFunctionPrototype('EXTRACT(%s FROM %s)'); 21 | $this->addNodeMapping('StringPrimary'); 22 | $this->addNodeMapping('StringPrimary'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/DBAL/Types/SmallIntArray.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class SmallIntArray extends BaseIntegerArray 16 | { 17 | /** 18 | * @var string 19 | */ 20 | protected const TYPE_NAME = 'smallint[]'; 21 | 22 | protected function getMinValue(): string 23 | { 24 | return '-32768'; 25 | } 26 | 27 | protected function getMaxValue(): string 28 | { 29 | return '32767'; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonGetFieldAsText.php: -------------------------------------------------------------------------------- 1 | >). 9 | * 10 | * @see https://www.postgresql.org/docs/9.4/static/functions-json.html 11 | * @since 0.1 12 | * 13 | * @author Martin Georgiev 14 | */ 15 | class JsonGetFieldAsText extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('(%s ->> %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbSet.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class JsonbSet extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('jsonb_set(%s, %s, %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | $this->addNodeMapping('StringPrimary'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbSetLax.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class JsonbSetLax extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('jsonb_set_lax(%s, %s, %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | $this->addNodeMapping('NewValue'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/DBAL/Types/IntegerArray.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class IntegerArray extends BaseIntegerArray 16 | { 17 | /** 18 | * @var string 19 | */ 20 | protected const TYPE_NAME = 'integer[]'; 21 | 22 | protected function getMinValue(): string 23 | { 24 | return '-2147483648'; 25 | } 26 | 27 | protected function getMaxValue(): string 28 | { 29 | return '2147483647'; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RegexpReplace.php: -------------------------------------------------------------------------------- 1 | setFunctionPrototype('regexp_replace(%s, %s, %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | $this->addNodeMapping('StringPrimary'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbInsert.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class JsonbInsert extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('jsonb_insert(%s, %s, %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | $this->addNodeMapping('StringPrimary'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonGetObjectAsText.php: -------------------------------------------------------------------------------- 1 | >). 9 | * 10 | * @see https://www.postgresql.org/docs/9.4/static/functions-json.html 11 | * @since 0.1 12 | * 13 | * @author Martin Georgiev 14 | */ 15 | class JsonGetObjectAsText extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('(%s #>> %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/TheRightExistsOnTheLeft.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class TheRightExistsOnTheLeft extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('(%s ?? %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/DBAL/Types/BigIntArray.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class BigIntArray extends BaseIntegerArray 16 | { 17 | /** 18 | * @var string 19 | */ 20 | protected const TYPE_NAME = 'bigint[]'; 21 | 22 | protected function getMinValue(): string 23 | { 24 | return '-9223372036854775807'; 25 | } 26 | 27 | protected function getMaxValue(): string 28 | { 29 | return '9223372036854775807'; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/DBAL/Types/Exceptions/UnexpectedTypeOfTransformedPHPValue.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class UnexpectedTypeOfTransformedPHPValue extends TypeException 13 | { 14 | public function __construct(string $untransformedValue, string $typeOfTransformedPHPValue) 15 | { 16 | $message = \sprintf( 17 | 'Transforming a PostgreSql value "%s" results to an unexpected PHP value from type "%s".', 18 | $untransformedValue, 19 | $typeOfTransformedPHPValue 20 | ); 21 | parent::__construct($message); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayReplace.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class ArrayReplace extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('array_replace(%s, %s, %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('ArithmeticPrimary'); 22 | $this->addNodeMapping('ArithmeticPrimary'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/AllOnTheRightExistOnTheLeft.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class AllOnTheRightExistOnTheLeft extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('(%s ??& %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ReturnsValueForJsonValue.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class ReturnsValueForJsonValue extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('(%s @?? %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/AnyOnTheRightExistsOnTheLeft.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class AnyOnTheRightExistsOnTheLeft extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('(%s ??| %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonGetFieldAsInteger.php: -------------------------------------------------------------------------------- 1 | > and type casting to BIGINT). 9 | * 10 | * @see https://www.postgresql.org/docs/9.4/static/functions-json.html 11 | * @since 0.3 12 | * 13 | * @author Martin Georgiev 14 | */ 15 | class JsonGetFieldAsInteger extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('CAST(%s ->> %s as BIGINT)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/FlaggedRegexpReplace.php: -------------------------------------------------------------------------------- 1 | setFunctionPrototype('regexp_replace(%s, %s, %s, %s)'); 20 | $this->addNodeMapping('StringPrimary'); 21 | $this->addNodeMapping('StringPrimary'); 22 | $this->addNodeMapping('StringPrimary'); 23 | $this->addNodeMapping('StringPrimary'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateOverlaps.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class DateOverlaps extends BaseFunction 16 | { 17 | protected function customiseFunction(): void 18 | { 19 | $this->setFunctionPrototype('(%s, %s) OVERLAPS (%s, %s)'); 20 | $this->addNodeMapping('StringExpression'); 21 | $this->addNodeMapping('StringExpression'); 22 | $this->addNodeMapping('StringExpression'); 23 | $this->addNodeMapping('StringExpression'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/FlaggedRegexpLike.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class FlaggedRegexpLike extends BaseFunction 17 | { 18 | protected function customiseFunction(): void 19 | { 20 | $this->setFunctionPrototype('regexp_like(%s, %s, 1, %s)'); 21 | $this->addNodeMapping('StringPrimary'); 22 | $this->addNodeMapping('StringPrimary'); 23 | $this->addNodeMapping('StringPrimary'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/FlaggedRegexpMatch.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class FlaggedRegexpMatch extends BaseFunction 17 | { 18 | protected function customiseFunction(): void 19 | { 20 | $this->setFunctionPrototype('regexp_match(%s, %s, %s)'); 21 | $this->addNodeMapping('StringPrimary'); 22 | $this->addNodeMapping('StringPrimary'); 23 | $this->addNodeMapping('StringPrimary'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2015 Martin Georgiev 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | The above copyright notice and this permission notice shall be included in all 10 | copies or substantial portions of the Software. 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/DBAL/Types/JsonTransformer.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | trait JsonTransformer 17 | { 18 | /** 19 | * @param mixed $phpValue Value must be suitable for JSON encoding 20 | * 21 | * @throws ConversionException When given value cannot be encoded 22 | */ 23 | protected function transformToPostgresJson(mixed $phpValue): string 24 | { 25 | try { 26 | $postgresValue = \json_encode($phpValue, JSON_THROW_ON_ERROR); 27 | } catch (\JsonException) { 28 | throw new ConversionException(\sprintf('Value %s can\'t be resolved to valid JSON', \var_export($phpValue, true))); 29 | } 30 | 31 | return $postgresValue; 32 | } 33 | 34 | protected function transformFromPostgresJson(string $postgresValue): null|array|bool|float|int|string 35 | { 36 | // @phpstan-ignore-next-line 37 | return \json_decode($postgresValue, true, 512, JSON_THROW_ON_ERROR); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/DBAL/Types/BaseType.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | abstract class BaseType extends Type 18 | { 19 | protected const TYPE_NAME = null; 20 | 21 | public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string 22 | { 23 | $this->throwExceptionIfTypeNameNotConfigured(); 24 | 25 | return $platform->getDoctrineTypeMapping(static::TYPE_NAME); 26 | } 27 | 28 | public function getName(): string 29 | { 30 | $this->throwExceptionIfTypeNameNotConfigured(); 31 | 32 | return static::TYPE_NAME; 33 | } 34 | 35 | /** 36 | * {@inheritdoc} 37 | */ 38 | public function requiresSQLCommentHint(AbstractPlatform $platform): bool 39 | { 40 | return false; 41 | } 42 | 43 | private function throwExceptionIfTypeNameNotConfigured(): void 44 | { 45 | if (null === static::TYPE_NAME) { 46 | throw new \LogicException(\sprintf('Doctrine type defined in class %s has no meaningful value for TYPE_NAME constant', self::class)); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/DBAL/Types/BooleanArray.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class BooleanArray extends BaseArray 18 | { 19 | /** 20 | * @var string 21 | */ 22 | protected const TYPE_NAME = 'bool[]'; 23 | 24 | public function convertToDatabaseValue($phpArray, AbstractPlatform $platform): ?string 25 | { 26 | if (\is_array($phpArray)) { 27 | $phpArray = $platform->convertBooleansToDatabaseValue($phpArray); 28 | } 29 | 30 | /* 31 | * $phpArray type will be checked by parent class 32 | * @phpstan-ignore-next-line 33 | */ 34 | return parent::convertToDatabaseValue($phpArray, $platform); 35 | } 36 | 37 | public function convertToPHPValue($postgresArray, AbstractPlatform $platform): ?array 38 | { 39 | $phpArray = parent::convertToPHPValue($postgresArray, $platform); 40 | if (!\is_array($phpArray)) { 41 | return null; 42 | } 43 | 44 | return \array_map(static fn ($value): ?bool => $platform->convertFromBoolean($value), $phpArray); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/DBAL/Types/Jsonb.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class Jsonb extends BaseType 18 | { 19 | use JsonTransformer; 20 | 21 | /** 22 | * @var string 23 | */ 24 | protected const TYPE_NAME = 'jsonb'; 25 | 26 | /** 27 | * Converts a value from its PHP representation to its database representation of the type. 28 | * 29 | * @param array|bool|float|int|string|null $value the value to convert 30 | */ 31 | public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string 32 | { 33 | if ($value === null) { 34 | return null; 35 | } 36 | 37 | return $this->transformToPostgresJson($value); 38 | } 39 | 40 | /** 41 | * Converts a value from its database representation to its PHP representation of this type. 42 | * 43 | * @param string|null $value the value to convert 44 | */ 45 | public function convertToPHPValue($value, AbstractPlatform $platform): null|array|bool|float|int|string 46 | { 47 | if ($value === null) { 48 | return null; 49 | } 50 | 51 | return $this->transformFromPostgresJson($value); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/DBAL/Types/BaseIntegerArray.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | abstract class BaseIntegerArray extends BaseArray 16 | { 17 | abstract protected function getMinValue(): string; 18 | 19 | abstract protected function getMaxValue(): string; 20 | 21 | /** 22 | * @param mixed $item 23 | */ 24 | public function isValidArrayItemForDatabase($item): bool 25 | { 26 | if (!\is_int($item) && !\is_string($item)) { 27 | return false; 28 | } 29 | if (!(bool) \preg_match('/^-?\d+$/', (string) $item)) { 30 | return false; 31 | } 32 | if ((string) $item < $this->getMinValue()) { 33 | return false; 34 | } 35 | 36 | return (string) $item <= $this->getMaxValue(); 37 | } 38 | 39 | /** 40 | * @param int|string|null $item Whole number 41 | */ 42 | public function transformArrayItemForPHP($item): ?int 43 | { 44 | if ($item === null) { 45 | return null; 46 | } 47 | 48 | $isInvalidPHPInt = !(bool) \preg_match('/^-?\d+$/', (string) $item) 49 | || (string) $item < $this->getMinValue() 50 | || (string) $item > $this->getMaxValue(); 51 | if ($isInvalidPHPInt) { 52 | throw new ConversionException(\sprintf('Given value of %s content cannot be transformed to valid PHP integer.', \var_export($item, true))); 53 | } 54 | 55 | return (int) $item; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/DBAL/Types/JsonbArray.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class JsonbArray extends BaseArray 18 | { 19 | use JsonTransformer; 20 | 21 | /** 22 | * @var string 23 | */ 24 | protected const TYPE_NAME = 'jsonb[]'; 25 | 26 | protected function transformArrayItemForPostgres(mixed $item): string 27 | { 28 | return $this->transformToPostgresJson($item); 29 | } 30 | 31 | protected function transformPostgresArrayToPHPArray(string $postgresArray): array 32 | { 33 | if ($postgresArray === '{}') { 34 | return []; 35 | } 36 | $trimmedPostgresArray = \mb_substr($postgresArray, 2, -2); 37 | $phpArray = \explode('},{', $trimmedPostgresArray); 38 | foreach ($phpArray as &$item) { 39 | $item = '{'.$item.'}'; 40 | } 41 | 42 | return $phpArray; 43 | } 44 | 45 | /** 46 | * @param string $item 47 | */ 48 | public function transformArrayItemForPHP($item): array 49 | { 50 | $transformedValue = null; 51 | 52 | try { 53 | $transformedValue = $this->transformFromPostgresJson($item); 54 | if (!\is_array($transformedValue)) { 55 | throw new UnexpectedTypeOfTransformedPHPValue($item, \gettype($transformedValue)); 56 | } 57 | } catch (\JsonException) { 58 | throw new UnexpectedTypeOfTransformedPHPValue($item, \gettype($transformedValue)); 59 | } 60 | 61 | return $transformedValue; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | abstract class BaseVariadicFunction extends BaseFunction 18 | { 19 | protected string $commonNodeMapping = 'StringPrimary'; 20 | 21 | public function feedParserWithNodes(Parser $parser): void 22 | { 23 | $lexer = $parser->getLexer(); 24 | 25 | $this->nodes[] = $parser->{$this->commonNodeMapping}(); 26 | if ($lexer->lookahead?->type === null) { 27 | throw new \RuntimeException('The parser\'s "lookahead" property is not populated with a type'); 28 | } 29 | 30 | $aheadType = $lexer->lookahead->type; 31 | $shouldUseLexer = DoctrineOrm::isPre219(); 32 | 33 | while (($shouldUseLexer ? Lexer::T_CLOSE_PARENTHESIS : TokenType::T_CLOSE_PARENTHESIS) !== $aheadType) { 34 | if (($shouldUseLexer ? Lexer::T_COMMA : TokenType::T_COMMA) === $aheadType) { 35 | $parser->match($shouldUseLexer ? Lexer::T_COMMA : TokenType::T_COMMA); 36 | $this->nodes[] = $parser->{$this->commonNodeMapping}(); 37 | } 38 | $aheadType = $lexer->lookahead->type; 39 | } 40 | } 41 | 42 | public function getSql(SqlWalker $sqlWalker): string 43 | { 44 | $dispatched = []; 45 | foreach ($this->nodes as $node) { 46 | $dispatched[] = $node instanceof Node ? $node->dispatch($sqlWalker) : 'null'; 47 | } 48 | 49 | return \sprintf($this->functionPrototype, \implode(', ', $dispatched)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/DBAL/Types/TextArray.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | class TextArray extends BaseType 19 | { 20 | /** 21 | * @var string 22 | */ 23 | protected const TYPE_NAME = 'text[]'; 24 | 25 | /** 26 | * Converts a value from its PHP representation to its database representation of the type. 27 | * 28 | * @param array|null $value the value to convert 29 | */ 30 | public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string 31 | { 32 | if ($value === null) { 33 | return null; 34 | } 35 | 36 | return $this->transformToPostgresTextArray($value); 37 | } 38 | 39 | protected function transformToPostgresTextArray(array $phpTextArray): string 40 | { 41 | if (!\is_array($phpTextArray)) { 42 | throw new \InvalidArgumentException(\sprintf('Value %s is not an array', \var_export($phpTextArray, true))); 43 | } 44 | if ($phpTextArray === []) { 45 | return '{}'; 46 | } 47 | 48 | return DataStructure::transformPHPArrayToPostgresTextArray($phpTextArray); 49 | } 50 | 51 | /** 52 | * Converts a value from its database representation to its PHP representation of this type. 53 | * 54 | * @param string|null $value the value to convert 55 | */ 56 | public function convertToPHPValue($value, AbstractPlatform $platform): ?array 57 | { 58 | if ($value === null) { 59 | return null; 60 | } 61 | 62 | return $this->transformFromPostgresTextArray($value); 63 | } 64 | 65 | protected function transformFromPostgresTextArray(string $postgresValue): array 66 | { 67 | if ($postgresValue === '{}') { 68 | return []; 69 | } 70 | 71 | return DataStructure::transformPostgresTextArrayToPHPArray($postgresValue); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base", 5 | ":enableVulnerabilityAlerts", 6 | ":semanticCommitsDisabled", 7 | ":prHourlyLimitNone", 8 | ":prImmediately" 9 | ], 10 | 11 | "automerge": true, 12 | "automergeStrategy": "squash", 13 | "automergeType": "pr", 14 | "branchPrefix": "renovate/", 15 | "lockFileMaintenance": { 16 | "enabled": true, 17 | "schedule": "before 11am every day" 18 | }, 19 | "platformAutomerge": true, 20 | "rangeStrategy": "in-range-only", 21 | "rebaseWhen": "conflicted", 22 | "recreateWhen": "never", 23 | 24 | "packageRules": [ 25 | { 26 | "matchPackagePatterns": ["*"], 27 | "enabled": false 28 | }, 29 | { 30 | "matchManagers": ["composer"], 31 | "matchDepTypes": ["require"], 32 | "enabled": true, 33 | "groupName": "Composer runtime dependencies", 34 | "groupSlug": "composer-runtime" 35 | }, 36 | { 37 | "matchManagers": ["composer"], 38 | "matchDepTypes": ["require-dev"], 39 | "enabled": true, 40 | "groupName": "Composer dev dependencies", 41 | "groupSlug": "composer-dev" 42 | }, 43 | { 44 | "matchManagers": ["composer"], 45 | "matchDepTypes": ["require-dev"], 46 | "enabled": true, 47 | "groupName": "Composer QA tooling dependencies", 48 | "groupSlug": "composer-qa-tooling", 49 | "rangeStrategy": "bump", 50 | "matchPackageNames": [ 51 | "friendsofphp/php-cs-fixer", 52 | "ekino/phpstan-banned-code", 53 | "qossmic/deptrac-shim", 54 | "php-coveralls/php-coveralls" 55 | ], 56 | "matchPackagePatterns": [ 57 | "phpstan/*", 58 | "phpunit/*", 59 | "rector/*" 60 | ] 61 | }, 62 | { 63 | "matchManagers": ["github-actions"], 64 | "enabled": true, 65 | "groupName": "GitHub Actions dependencies", 66 | "groupSlug": "github-actions", 67 | "rangeStrategy": "bump" 68 | } 69 | ] 70 | } 71 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/StringAgg.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | class StringAgg extends BaseFunction 24 | { 25 | private bool $isDistinct = false; 26 | 27 | private Node $expression; 28 | 29 | private Node $delimiter; 30 | 31 | private OrderByClause $orderByClause; 32 | 33 | protected function customiseFunction(): void 34 | { 35 | $this->setFunctionPrototype('string_agg(%s%s, %s%s)'); 36 | } 37 | 38 | public function parse(Parser $parser): void 39 | { 40 | $shouldUseLexer = DoctrineOrm::isPre219(); 41 | 42 | $this->customiseFunction(); 43 | 44 | $parser->match($shouldUseLexer ? Lexer::T_IDENTIFIER : TokenType::T_IDENTIFIER); 45 | $parser->match($shouldUseLexer ? Lexer::T_OPEN_PARENTHESIS : TokenType::T_OPEN_PARENTHESIS); 46 | 47 | $lexer = $parser->getLexer(); 48 | if ($lexer->isNextToken($shouldUseLexer ? Lexer::T_DISTINCT : TokenType::T_DISTINCT)) { 49 | $parser->match($shouldUseLexer ? Lexer::T_DISTINCT : TokenType::T_DISTINCT); 50 | $this->isDistinct = true; 51 | } 52 | 53 | $this->expression = $parser->StringPrimary(); 54 | $parser->match($shouldUseLexer ? Lexer::T_COMMA : TokenType::T_COMMA); 55 | $this->delimiter = $parser->StringPrimary(); 56 | 57 | if ($lexer->isNextToken($shouldUseLexer ? Lexer::T_ORDER : TokenType::T_ORDER)) { 58 | $this->orderByClause = $parser->OrderByClause(); 59 | } 60 | 61 | $parser->match($shouldUseLexer ? Lexer::T_CLOSE_PARENTHESIS : TokenType::T_CLOSE_PARENTHESIS); 62 | } 63 | 64 | public function getSql(SqlWalker $sqlWalker): string 65 | { 66 | $dispatched = [ 67 | $this->isDistinct ? 'distinct ' : '', 68 | $this->expression->dispatch($sqlWalker), 69 | $this->delimiter->dispatch($sqlWalker), 70 | isset($this->orderByClause) ? $this->orderByClause->dispatch($sqlWalker) : '', 71 | ]; 72 | 73 | return \vsprintf($this->functionPrototype, $dispatched); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseFunction.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | abstract class BaseFunction extends FunctionNode 21 | { 22 | protected string $functionPrototype; 23 | 24 | /** 25 | * @var list 26 | */ 27 | protected array $nodesMapping = []; 28 | 29 | /** 30 | * @var list 31 | */ 32 | protected array $nodes = []; 33 | 34 | abstract protected function customiseFunction(): void; 35 | 36 | protected function setFunctionPrototype(string $functionPrototype): void 37 | { 38 | $this->functionPrototype = $functionPrototype; 39 | } 40 | 41 | protected function addNodeMapping(string $parserMethod): void 42 | { 43 | $this->nodesMapping[] = $parserMethod; 44 | } 45 | 46 | public function parse(Parser $parser): void 47 | { 48 | $shouldUseLexer = DoctrineOrm::isPre219(); 49 | 50 | $this->customiseFunction(); 51 | 52 | $parser->match($shouldUseLexer ? Lexer::T_IDENTIFIER : TokenType::T_IDENTIFIER); 53 | $parser->match($shouldUseLexer ? Lexer::T_OPEN_PARENTHESIS : TokenType::T_OPEN_PARENTHESIS); 54 | $this->feedParserWithNodes($parser); 55 | $parser->match($shouldUseLexer ? Lexer::T_CLOSE_PARENTHESIS : TokenType::T_CLOSE_PARENTHESIS); 56 | } 57 | 58 | /** 59 | * Feeds given parser with previously set nodes. 60 | */ 61 | protected function feedParserWithNodes(Parser $parser): void 62 | { 63 | $nodesMappingCount = \count($this->nodesMapping); 64 | $lastNode = $nodesMappingCount - 1; 65 | for ($i = 0; $i < $nodesMappingCount; $i++) { 66 | $parserMethod = $this->nodesMapping[$i]; 67 | $this->nodes[$i] = $parser->{$parserMethod}(); 68 | if ($i < $lastNode) { 69 | $parser->match(\class_exists(TokenType::class) ? TokenType::T_COMMA : Lexer::T_COMMA); 70 | } 71 | } 72 | } 73 | 74 | public function getSql(SqlWalker $sqlWalker): string 75 | { 76 | $dispatched = []; 77 | foreach ($this->nodes as $node) { 78 | $dispatched[] = $node instanceof Node ? $node->dispatch($sqlWalker) : 'null'; 79 | } 80 | 81 | return \vsprintf($this->functionPrototype, $dispatched); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | tests: 14 | name: "PHP ${{ matrix.php }} + Doctrine ORM ${{ matrix.doctrine-orm }} + Composer ${{ matrix.composer-flags }}" 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | include: 21 | - php: '8.1' 22 | - php: '8.2' 23 | - php: '8.3' 24 | - php: '8.4' 25 | ignore-php-version: true 26 | calculate-code-coverage: true 27 | - doctrine-orm: '2.14' 28 | - doctrine-orm: '3.0' 29 | - doctrine-orm: 'latest' 30 | 31 | steps: 32 | - uses: actions/checkout@v4 33 | 34 | - name: Set up PHP with PECL extension 35 | uses: shivammathur/setup-php@v2 36 | with: 37 | php-version: ${{ matrix.php || '8.4' }} 38 | coverage: xdebug 39 | extensions: ctype, json, mbstring 40 | tools: composer 41 | 42 | - name: Validate composer.json and composer.lock 43 | run: composer validate --strict 44 | 45 | - name: Cache Composer packages 46 | id: composer-cache 47 | uses: actions/cache@v4 48 | with: 49 | path: vendor 50 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} 51 | restore-keys: | 52 | ${{ runner.os }}-php- 53 | 54 | - name: Install composer dependencies 55 | run: | 56 | if [ "${{ matrix.doctrine-orm }}" == "2.14" ]; then 57 | composer require doctrine/orm "~2.14" --prefer-dist --no-interaction --no-progress ${{ matrix.composer-flags }} 58 | elif [ "${{ matrix.doctrine-orm }}" == "3.0" ]; then 59 | composer require doctrine/orm "~3.0" --prefer-dist --no-interaction --no-progress ${{ matrix.composer-flags }} 60 | else 61 | composer update --prefer-dist --no-interaction --no-progress ${{ matrix.composer-flags }} 62 | fi 63 | 64 | - name: Run static analysis 65 | run: composer run-static-analysis 66 | continue-on-error: ${{ matrix.continue-on-error || false }} 67 | 68 | - name: Check code style 69 | run: composer check-code-style 70 | if: matrix.ignore-php-version == true 71 | env: 72 | PHP_CS_FIXER_IGNORE_ENV: 1 73 | 74 | - name: Check for security vulnerabilities in 3rd party dependencies 75 | run: composer audit 76 | 77 | - name: Run test suite 78 | run: composer run-tests-with-clover 79 | 80 | - name: Upload coverage results to Coveralls 81 | if: matrix.calculate-code-coverage == true 82 | env: 83 | COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} 84 | COVERALLS_PARALLEL: true 85 | COVERALLS_FLAG_NAME: "PHP ${{ matrix.php }} + Composer@${{ matrix.composer-flags || '*' }}" 86 | run: bin/php-coveralls -v --exclude-no-stmt 87 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Cast.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | class Cast extends FunctionNode 26 | { 27 | public Node $sourceType; 28 | 29 | public string $targetType; 30 | 31 | public function parse(Parser $parser): void 32 | { 33 | $shouldUseLexer = DoctrineOrm::isPre219(); 34 | 35 | $parser->match($shouldUseLexer ? Lexer::T_IDENTIFIER : TokenType::T_IDENTIFIER); 36 | $parser->match($shouldUseLexer ? Lexer::T_OPEN_PARENTHESIS : TokenType::T_OPEN_PARENTHESIS); 37 | $this->sourceType = $parser->SimpleArithmeticExpression(); 38 | $parser->match($shouldUseLexer ? Lexer::T_AS : TokenType::T_AS); 39 | $parser->match($shouldUseLexer ? Lexer::T_IDENTIFIER : TokenType::T_IDENTIFIER); 40 | 41 | $lexer = $parser->getLexer(); 42 | $token = $lexer->token; 43 | if (!$token instanceof Token) { 44 | return; 45 | } 46 | if (!\is_string($token->value)) { 47 | return; 48 | } 49 | 50 | $type = $token->value; 51 | if ($lexer->isNextToken($shouldUseLexer ? Lexer::T_OPEN_PARENTHESIS : TokenType::T_OPEN_PARENTHESIS)) { 52 | $parser->match($shouldUseLexer ? Lexer::T_OPEN_PARENTHESIS : TokenType::T_OPEN_PARENTHESIS); 53 | $parameter = $parser->Literal(); 54 | $parameters = [$parameter->value]; 55 | if ($lexer->isNextToken($shouldUseLexer ? Lexer::T_COMMA : TokenType::T_COMMA)) { 56 | while ($lexer->isNextToken($shouldUseLexer ? Lexer::T_COMMA : TokenType::T_COMMA)) { 57 | $parser->match($shouldUseLexer ? Lexer::T_COMMA : TokenType::T_COMMA); 58 | $parameter = $parser->Literal(); 59 | $parameters[] = $parameter->value; 60 | } 61 | } 62 | $parser->match($shouldUseLexer ? Lexer::T_CLOSE_PARENTHESIS : TokenType::T_CLOSE_PARENTHESIS); 63 | $type .= '('.\implode(', ', $parameters).')'; 64 | } 65 | 66 | $this->targetType = $type; 67 | 68 | $parser->match($shouldUseLexer ? Lexer::T_CLOSE_PARENTHESIS : TokenType::T_CLOSE_PARENTHESIS); 69 | } 70 | 71 | public function getSql(SqlWalker $sqlWalker): string 72 | { 73 | return \sprintf('cast(%s as %s)', $this->sourceType->dispatch($sqlWalker), $this->targetType); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "martin-georgiev/postgresql-for-doctrine", 3 | "type": "library", 4 | "description": "Adds PostgreSQL enhancements to Doctrine. Provides support for JSON, JSONB and some array data types. Provides functions, operators and common expressions used when working with JSON data, arrays and features related to text search.", 5 | "keywords": [ 6 | "martin georgiev", 7 | "doctrine", 8 | "postgresql", 9 | "postgres", 10 | "dbal", 11 | "json", 12 | "jsonb", 13 | "text search", 14 | "tsvector", 15 | "array data types" 16 | ], 17 | "license": "MIT", 18 | "authors": [ 19 | { 20 | "name": "Martin Georgiev", 21 | "email": "martin.georgiev@gmail.com", 22 | "role": "author" 23 | } 24 | ], 25 | 26 | "autoload": { 27 | "psr-4": { 28 | "MartinGeorgiev\\": "src/MartinGeorgiev/" 29 | } 30 | }, 31 | "autoload-dev": { 32 | "psr-4": { 33 | "Fixtures\\MartinGeorgiev\\": "fixtures/MartinGeorgiev/", 34 | "Tests\\MartinGeorgiev\\": "tests/MartinGeorgiev/" 35 | } 36 | }, 37 | 38 | "require": { 39 | "php": "^8.1", 40 | "ext-ctype": "*", 41 | "ext-json": "*", 42 | "ext-mbstring": "*", 43 | "doctrine/dbal": "~2.10||~3.0||~4.0" 44 | }, 45 | "require-dev": { 46 | "doctrine/orm": "~2.14||~3.0", 47 | "ekino/phpstan-banned-code": "^1.0", 48 | "friendsofphp/php-cs-fixer": "^3.67.0", 49 | "php-coveralls/php-coveralls": "^2.7.0", 50 | "phpstan/phpstan": "^1.12.15", 51 | "phpstan/phpstan-phpunit": "^1.4.2", 52 | "phpunit/phpunit": "^10.5.40", 53 | "qossmic/deptrac": "^2.0.4", 54 | "rector/rector": "^1.2.10", 55 | "symfony/cache": "^6.4||^7.0" 56 | }, 57 | "suggest": { 58 | "php": "^8.3", 59 | "doctrine/orm": "~2.14||~3.0" 60 | }, 61 | 62 | "scripts": { 63 | "check-code-style": [ 64 | "php-cs-fixer fix --config='./ci/php-cs-fixer/config.php' --show-progress=none --dry-run --no-interaction --diff -v", 65 | "rector --config='./ci/rector/config.php' --ansi --no-progress-bar --dry-run" 66 | ], 67 | "fix-code-style": [ 68 | "rector --config='./ci/rector/config.php' --ansi --no-progress-bar", 69 | "php-cs-fixer fix --config='./ci/php-cs-fixer/config.php' --show-progress=none --no-interaction --diff -v" 70 | ], 71 | "run-static-analysis": [ 72 | "phpstan analyse --configuration='./ci/phpstan/config.neon'", 73 | "deptrac analyze --config-file='./ci/deptrac/config.yml' --cache-file='./ci/deptrac/.cache' --no-interaction --no-progress" 74 | ], 75 | "run-tests": [ 76 | "phpunit --configuration='./ci/phpunit/config.xml'" 77 | ], 78 | "run-tests-with-clover": [ 79 | "phpunit --configuration='./ci/phpunit/config.xml' --coverage-clover './build/logs/clover.xml'" 80 | ] 81 | }, 82 | 83 | "config": { 84 | "bin-dir": "bin", 85 | "sort-packages": true 86 | }, 87 | "prefer-stable": true 88 | } 89 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Utils/DataStructure.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class DataStructure 15 | { 16 | /** 17 | * This method supports only single-dimensioned text arrays and 18 | * relays on the default escaping strategy in PostgreSql (double quotes). 19 | */ 20 | public static function transformPostgresTextArrayToPHPArray(string $postgresArray): array 21 | { 22 | $transform = static function (string $textArrayToTransform): array { 23 | $indicatesMultipleDimensions = \mb_strpos($textArrayToTransform, '},{') !== false 24 | || \mb_strpos($textArrayToTransform, '{{') === 0; 25 | if ($indicatesMultipleDimensions) { 26 | throw new \InvalidArgumentException('Only single-dimensioned arrays are supported'); 27 | } 28 | 29 | $phpArray = \str_getcsv(\trim($textArrayToTransform, '{}'), escape: '\\'); 30 | foreach ($phpArray as $i => $text) { 31 | if ($text === null) { 32 | unset($phpArray[$i]); 33 | 34 | break; 35 | } 36 | 37 | $isInteger = \is_numeric($text) && ''.(int) $text === $text; 38 | if ($isInteger) { 39 | $phpArray[$i] = (int) $text; 40 | 41 | continue; 42 | } 43 | 44 | $isFloat = \is_numeric($text) && ''.(float) $text === $text; 45 | if ($isFloat) { 46 | $phpArray[$i] = (float) $text; 47 | 48 | continue; 49 | } 50 | 51 | if (!\is_string($text)) { 52 | $exceptionMessage = 'Unsupported data type encountered. Expected null, integer, float or string value. Instead it is "%s".'; 53 | 54 | throw new \InvalidArgumentException(\sprintf($exceptionMessage, \gettype($text))); 55 | } 56 | 57 | $phpArray[$i] = \stripslashes(\str_replace('\"', '"', $text)); 58 | } 59 | 60 | return $phpArray; 61 | }; 62 | 63 | return $transform($postgresArray); 64 | } 65 | 66 | /** 67 | * This method supports only single-dimensioned PHP arrays. 68 | * This method relays on the default escaping strategy in PostgreSql (double quotes). 69 | * 70 | * @see https://stackoverflow.com/a/5632171/3425372 Kudos to jmz for the inspiration 71 | */ 72 | public static function transformPHPArrayToPostgresTextArray(array $phpArray): string 73 | { 74 | $transform = static function (array $phpArrayToTransform): string { 75 | $result = []; 76 | foreach ($phpArrayToTransform as $text) { 77 | if (\is_array($text)) { 78 | throw new \InvalidArgumentException('Only single-dimensioned arrays are supported'); 79 | } 80 | 81 | $escapedText = \is_numeric($text) || \ctype_digit($text) ? $text : \sprintf('"%s"', \addcslashes($text, '"\\')); 82 | $result[] = $escapedText; 83 | } 84 | 85 | return '{'.\implode(',', $result).'}'; 86 | }; 87 | 88 | return $transform($phpArray); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/MartinGeorgiev/Doctrine/DBAL/Types/BaseArray.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | abstract class BaseArray extends BaseType 19 | { 20 | /** 21 | * Converts a value from its PHP representation to its PostgreSql representation of the type. 22 | * 23 | * @param array|null $phpArray the value to convert 24 | * 25 | * @throws ConversionException When passed argument is not PHP array OR When invalid array items are detected 26 | */ 27 | public function convertToDatabaseValue($phpArray, AbstractPlatform $platform): ?string 28 | { 29 | if ($phpArray === null) { 30 | return null; 31 | } 32 | 33 | if (!\is_array($phpArray)) { 34 | $exceptionMessage = 'Given PHP value content type is not PHP array. Instead it is "%s".'; 35 | 36 | throw new \InvalidArgumentException(\sprintf($exceptionMessage, \gettype($phpArray))); 37 | } 38 | 39 | foreach ($phpArray as &$item) { 40 | if (!$this->isValidArrayItemForDatabase($item)) { 41 | $exceptionMessage = 'One or more of items given doesn\'t look like valid.'; 42 | 43 | throw new ConversionException($exceptionMessage); 44 | } 45 | $item = $this->transformArrayItemForPostgres($item); 46 | } 47 | 48 | return '{'.\implode(',', $phpArray).'}'; 49 | } 50 | 51 | /** 52 | * Tests if given PHP array item is from compatible type for PostgreSql. 53 | */ 54 | protected function isValidArrayItemForDatabase(mixed $item): bool 55 | { 56 | return true; 57 | } 58 | 59 | /** 60 | * Transforms PHP array item to a PostgreSql compatible array item. 61 | * 62 | * @return mixed 63 | */ 64 | protected function transformArrayItemForPostgres(mixed $item) 65 | { 66 | return $item; 67 | } 68 | 69 | /** 70 | * Converts a value from its PostgreSql representation to its PHP representation of this type. 71 | * 72 | * @param string|null $postgresArray the value to convert 73 | */ 74 | public function convertToPHPValue($postgresArray, AbstractPlatform $platform): ?array 75 | { 76 | if ($postgresArray === null) { 77 | return null; 78 | } 79 | if (!\is_string($postgresArray)) { 80 | $exceptionMessage = 'Given PostgreSql value content type is not PHP string. Instead it is "%s".'; 81 | 82 | throw new ConversionException(\sprintf($exceptionMessage, \gettype($postgresArray))); 83 | } 84 | 85 | $phpArray = $this->transformPostgresArrayToPHPArray($postgresArray); 86 | foreach ($phpArray as &$item) { 87 | $item = $this->transformArrayItemForPHP($item); 88 | } 89 | 90 | return $phpArray; 91 | } 92 | 93 | protected function transformPostgresArrayToPHPArray(string $postgresArray): array 94 | { 95 | $trimmedPostgresArray = \mb_substr($postgresArray, 1, -1); 96 | if ($trimmedPostgresArray === '') { 97 | return []; 98 | } 99 | 100 | return \explode(',', $trimmedPostgresArray); 101 | } 102 | 103 | /** 104 | * Transforms PostgreSql array item to a PHP compatible array item. 105 | * 106 | * @return mixed 107 | */ 108 | protected function transformArrayItemForPHP(mixed $item) 109 | { 110 | return $item; 111 | } 112 | } 113 | --------------------------------------------------------------------------------