{"id":14973978,"url":"https://github.com/sad-spirit/pg-wrapper","last_synced_at":"2025-10-27T05:31:21.684Z","repository":{"id":20948778,"uuid":"24237259","full_name":"sad-spirit/pg-wrapper","owner":"sad-spirit","description":"Converter of complex PostgreSQL types and an OO wrapper for PHP's pgsql extension","archived":false,"fork":false,"pushed_at":"2024-09-28T13:25:50.000Z","size":907,"stargazers_count":18,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-10-30T06:58:24.264Z","etag":null,"topics":["array","complex-types","composite","datetime","geometric","hstore","interval","json","pdo","pgsql","php","postgres","postgresql","range"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sad-spirit.png","metadata":{"files":{"readme":"README.md","changelog":"Changelog.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-09-19T16:36:40.000Z","updated_at":"2024-09-28T13:25:54.000Z","dependencies_parsed_at":"2024-05-17T15:35:28.747Z","dependency_job_id":"fa543105-88cb-498a-8707-a86342d93ccd","html_url":"https://github.com/sad-spirit/pg-wrapper","commit_stats":{"total_commits":252,"total_committers":2,"mean_commits":126.0,"dds":0.003968253968253954,"last_synced_commit":"e87fb9582ac94a067fe5cf38364c8befc4ce9379"},"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sad-spirit%2Fpg-wrapper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sad-spirit%2Fpg-wrapper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sad-spirit%2Fpg-wrapper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sad-spirit%2Fpg-wrapper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sad-spirit","download_url":"https://codeload.github.com/sad-spirit/pg-wrapper/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238445831,"owners_count":19473820,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["array","complex-types","composite","datetime","geometric","hstore","interval","json","pdo","pgsql","php","postgres","postgresql","range"],"created_at":"2024-09-24T13:49:46.334Z","updated_at":"2025-10-27T05:31:21.677Z","avatar_url":"https://github.com/sad-spirit.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# sad_spirit/pg_wrapper\n\n[![Build Status](https://github.com/sad-spirit/pg-wrapper/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/sad-spirit/pg-wrapper/actions/workflows/ci.yml)\n[![Static Analysis](https://github.com/sad-spirit/pg-wrapper/actions/workflows/static-analysis.yml/badge.svg?branch=master)](https://github.com/sad-spirit/pg-wrapper/actions/workflows/static-analysis.yml)\n\nThis package has two parts and purposes\n* Converter of [PostgreSQL data types](https://www.postgresql.org/docs/current/datatype.html) to their PHP equivalents and back and\n* An object-oriented wrapper around PHP's native [pgsql extension](https://php.net/manual/en/book.pgsql.php).\n\nWhile the converter part can be used separately e.g. with [PDO](https://www.php.net/manual/en/book.pdo.php), \nfeatures like transparent conversion of query results work only with the wrapper.\n\n## Why type conversion?\n\nPostgreSQL supports a large (and extensible) set of complex database types: arrays, ranges, geometric and date/time\ntypes, composite (row) types, JSON...\n\n```SQL\ncreate table test (\n    strings  text[],\n    coords   point,\n    occupied daterange,\n    age      interval,\n    document json\n);\n\ninsert into test values (\n    array['Mary had', 'a little lamb'], point(55.75, 37.61),\n    daterange('2014-01-13', '2014-09-19'), age('2014-09-19', '2014-01-13'),\n    '{\"title\":\"pg_wrapper\",\"text\":\"pg_wrapper is cool\"}'\n);\n```\n\nUnfortunately neither of PHP extensions for talking to PostgreSQL (pgsql and PDO_pgsql) can map these complex\ntypes to their PHP equivalents. They return string representations instead: both\n```PHP\nvar_dump(pg_fetch_assoc(pg_query($conn, 'select * from test')));\n```\nand\n```PHP\nvar_dump($pdo-\u003equery('select * from test')-\u003efetch(\\PDO::FETCH_ASSOC));\n```\nyield\n```\narray(5) {\n  [\"strings\"]=\u003e\n  string(28) \"{\"Mary had\",\"a little lamb\"}\"\n  [\"coords\"]=\u003e\n  string(13) \"(55.75,37.61)\"\n  [\"occupied\"]=\u003e\n  string(23) \"[2014-01-13,2014-09-19)\"\n  [\"age\"]=\u003e\n  string(13) \"8 mons 6 days\"\n  [\"document\"]=\u003e\n  string(50) \"{\"title\":\"pg_wrapper\",\"text\":\"pg_wrapper is cool\"}\"\n}\n```\n\nAnd that is where this library kicks in:\n```PHP\n$result = $connection-\u003eexecute('select * from test');\nvar_dump($result[0]);\n```\nyields\n```\narray(5) {\n  [\"strings\"]=\u003e\n  array(2) {\n    [0]=\u003e\n    string(8) \"Mary had\"\n    [1]=\u003e\n    string(13) \"a little lamb\"\n  }\n  [\"coords\"]=\u003e\n  object(sad_spirit\\pg_wrapper\\types\\Point)#28 (2) {\n    [\"x\"]=\u003e\n    float(55.75)\n    [\"y\"]=\u003e\n    float(37.61)\n  }\n  [\"occupied\"]=\u003e\n  object(sad_spirit\\pg_wrapper\\types\\DateTimeRange)#29 (5) {\n    [\"lower\"]=\u003e\n    object(DateTimeImmutable)#30 (3) {\n      [\"date\"]=\u003e\n      string(26) \"2014-01-13 00:00:00.000000\"\n      ...\n    }\n    [\"upper\"]=\u003e\n    object(DateTimeImmutable)#31 (3) {\n      [\"date\"]=\u003e\n      string(26) \"2014-09-19 00:00:00.000000\"\n      ...\n    }\n  }\n  [\"age\"]=\u003e\n  object(DateInterval)#32 (10) {\n    ...\n    [\"m\"]=\u003e\n    int(8)\n    [\"d\"]=\u003e\n    int(6)\n    ...\n  }\n  [\"document\"]=\u003e\n  array(2) {\n    [\"title\"]=\u003e\n    string(10) \"pg_wrapper\"\n    [\"text\"]=\u003e\n    string(18) \"pg_wrapper is cool\"\n  }\n}\n```\nNote that no configuration is needed here: proper types are deduced from metadata returned with the result.\n\n## Why another wrapper when we have PDO, Doctrine DBAL, etc?\n\nThe goal of an abstraction layer is to target the Lowest Common Denominator, and thus it intentionally hides some low-level\nAPIs that we can use with the native extension and / or adds another level of complexity.\n\n* PDO does not expose [`pg_query_params()`](http://php.net/manual/en/function.pg-query-params.php), so you have\n  to `prepare()` / `execute()` each query even if you `execute()` it only once. Doctrine DBAL has `Connection::executeQuery()`\n  but it uses `prepare()` / `execute()` under the hood.\n* Postgres only supports `$1` positional parameters natively, while PDO has positional `?` and named `:foo` parameters.\n  PDO rewrites the query to convert the latter to the former, shortcomings in that rewrite logic \n  prevented using [Postgres operators containing `?`](https://www.postgresql.org/docs/current/functions-json.html#FUNCTIONS-JSONB-OP-TABLE) with\n  PDO [until PHP 7.4](https://wiki.php.net/rfc/pdo_escape_placeholders) and led to problems when using dollar quoting for strings\n  [until PHP 8.4](https://www.php.net/manual/en/migration84.new-features.php#migration84.new-features.pdo-pgsql).\n* PDO has an *extremely inefficient* way to work with result metadata in Postgres. Its \n  [`PDOStatement::getColumnMeta()`](https://www.php.net/manual/en/pdostatement.getcolumnmeta.php)\n  executes one to two database queries for each call.\n* The default way [Doctrine handles date and time types in PostgreSQL](https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/known-vendor-issues.html#datetime-datetimetz-and-time-types)\n  is prominently mentioned in [Don't Do This](https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_timestamp.280.29_or_timestamptz.280.29) list on PostgreSQL's official wiki.\n\n### Parameter as array: native vs. abstraction\n\nA very common problem for database abstraction is providing a list of parameters to a query with an `IN` clause\n```SQL\nSELECT * FROM stuff WHERE id IN (?)\n```\nwhere `?` actually represents a variable number of parameters.\n\nOn the one hand, if you don't need the abstraction, then Postgres has native array types,\nand this can be easily achieved with the following query\n```SQL\n-- in case of using PDO just replace $1 with a PDO-compatible placeholder\nSELECT * FROM stuff WHERE id = ANY($1::INTEGER[])\n```\npassing an array literal as its parameter value\n```PHP\nuse sad_spirit\\pg_wrapper\\converters\\DefaultTypeConverterFactory;\n\n$arrayLiteral = (new DefaultTypeConverterFactory())\n    -\u003egetConverterForTypeSpecification('INTEGER[]')\n    -\u003eoutput([1, 2, 3]);\n```\nObviously, the above query can be prepared as usual and executed with another array literal.\n\nOn the other hand, Doctrine DBAL [has its own solution for parameter lists](https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/data-retrieval-and-manipulation.html#list-of-parameters-conversion)\nwhich once again depends on rewriting SQL and does not work with `prepare()` / `execute()`. It also has [\"support\" for array\ntypes](https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/types.html#array-types), \nbut that just (un)serializes PHP arrays rather than converts them from/to native DB representation. \nSerialized PHP arrays will obviously not work with the above query.\n\n## Installation\n\nRequire the package with composer:\n```\ncomposer require \"sad_spirit/pg_wrapper:^3\"\n```\npg_wrapper requires at least PHP 8.2. Native [pgsql extension](https://php.net/manual/en/book.pgsql.php)\nshould be enabled to use classes that access the DB (the extension is not a hard requirement).\n\nMinimum supported PostgreSQL version is 12\n\nIt is highly recommended to use [PSR-6 compatible](https://www.php-fig.org/psr/psr-6/) metadata cache in production\nto prevent possible metadata lookups from database on each page request.\n\n## Documentation\n\nFor in-depth description of package features, visit [pg_wrapper manual](https://pg-wrapper.readthedocs.io/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsad-spirit%2Fpg-wrapper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsad-spirit%2Fpg-wrapper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsad-spirit%2Fpg-wrapper/lists"}