data mapper POC
authorMichael Wallner <mike@php.net>
Sat, 19 Sep 2015 07:05:50 +0000 (09:05 +0200)
committerMichael Wallner <mike@php.net>
Sat, 19 Sep 2015 07:05:50 +0000 (09:05 +0200)
19 files changed:
.gitignore
README.md
composer.json
composer.lock [new file with mode: 0644]
lib/pq/Gateway/Table/Reference.php
lib/pq/Mapper/Map.php [new file with mode: 0644]
lib/pq/Mapper/MapInterface.php [new file with mode: 0644]
lib/pq/Mapper/Mapper.php [new file with mode: 0644]
lib/pq/Mapper/ObjectCache.php [new file with mode: 0644]
lib/pq/Mapper/Property.php [new file with mode: 0644]
lib/pq/Mapper/Property/All.php [new file with mode: 0644]
lib/pq/Mapper/Property/Field.php [new file with mode: 0644]
lib/pq/Mapper/Property/Ref.php [new file with mode: 0644]
lib/pq/Mapper/PropertyInterface.php [new file with mode: 0644]
lib/pq/Mapper/RefProperty.php [new file with mode: 0644]
lib/pq/Mapper/RefPropertyInterface.php [new file with mode: 0644]
lib/pq/Mapper/Storage.php [new file with mode: 0644]
lib/pq/Mapper/StorageInterface.php [new file with mode: 0644]
pq-stub.php [new file with mode: 0644]

index ff6a0c33da37a4cd441b100f5d0ae13f51c41525..9b81c056de28259ad1977a0b5addabe7789d3645 100644 (file)
@@ -1,2 +1,3 @@
 /nbproject/
 tests/query.log
 /nbproject/
 tests/query.log
+vendor
index 0b8d5cd355fc864d8c78c66a76b12d795a69f23e..1ac5b4ceeb269167630b65b8a9f8ef9ef92767c1 100644 (file)
--- a/README.md
+++ b/README.md
@@ -8,12 +8,18 @@ for [ext-pq](http://git.php.net/?p=pecl/database/pq.git;a=summary).
 http://devel-m6w6.rhcloud.com/mdref/pq-gateway\r
 \r
 ## News\r
 http://devel-m6w6.rhcloud.com/mdref/pq-gateway\r
 \r
 ## News\r
+* ***2015-05-20:*** 2.1.0 tagged\r
 * ***2014-10-15:*** 2.0.0 tagged\r
 * ***2013-05-15:*** 1.1.0 tagged\r
 * ***2013-05-03:*** 1.0.0 tagged\r
 \r
 ## ChangeLog\r
 \r
 * ***2014-10-15:*** 2.0.0 tagged\r
 * ***2013-05-15:*** 1.1.0 tagged\r
 * ***2013-05-03:*** 1.0.0 tagged\r
 \r
 ## ChangeLog\r
 \r
+### 2.1.0\r
+* Added pq\Query\AsyncExecutor::setCallbacks(callable $init, callable $done, callable $then)  \r
+  and removed soft dependency on reactphp/promise\r
+* Fixed pq\Gateway\Table::with()'s relation handling when source table equals foreign table\r
+\r
 ### 2.0.0\r
 * Published documentation\r
 * Added support for pecl/pq-0.5\r
 ### 2.0.0\r
 * Published documentation\r
 * Added support for pecl/pq-0.5\r
index 2fd2af6f4e95bb0ecaf9999d696ec05ff5b95f59..5c87f36cb8ca95ee7e8028e02f756695e7269323 100644 (file)
@@ -14,7 +14,8 @@
        "autoload": {
                "psr-0": {
                        "pq\\Gateway": "lib",
        "autoload": {
                "psr-0": {
                        "pq\\Gateway": "lib",
-                       "pq\\Query": "lib"
+                       "pq\\Query": "lib",
+                       "pq\\Data": "lib"
                }
        },
        "require-dev": {
                }
        },
        "require-dev": {
diff --git a/composer.lock b/composer.lock
new file mode 100644 (file)
index 0000000..7489369
--- /dev/null
@@ -0,0 +1,122 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+        "This file is @generated automatically"
+    ],
+    "hash": "813817d14a924e8b4189c1365120cf86",
+    "packages": [],
+    "packages-dev": [
+        {
+            "name": "amphp/amp",
+            "version": "v1.0.0-beta4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/amphp/amp.git",
+                "reference": "9fa6010f192f82a81381ae2dd372e1e75107d332"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/amphp/amp/zipball/9fa6010f192f82a81381ae2dd372e1e75107d332",
+                "reference": "9fa6010f192f82a81381ae2dd372e1e75107d332",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.5"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.4.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0.0-dev",
+                    "dev-v1.0.x": "1.0.0-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Amp\\": "lib/"
+                },
+                "files": [
+                    "lib/functions.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Daniel Lowrey",
+                    "email": "rdlowrey@php.net",
+                    "role": "Creator / Lead Developer"
+                }
+            ],
+            "description": "A non-blocking concurrency framework for PHP applications",
+            "homepage": "https://github.com/amphp/amp",
+            "keywords": [
+                "async",
+                "concurrency",
+                "event",
+                "future",
+                "non-blocking",
+                "promise"
+            ],
+            "time": "2015-05-20 03:12:32"
+        },
+        {
+            "name": "react/promise",
+            "version": "v2.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/promise.git",
+                "reference": "365fcee430dfa4ace1fbc75737ca60ceea7eeeef"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/promise/zipball/365fcee430dfa4ace1fbc75737ca60ceea7eeeef",
+                "reference": "365fcee430dfa4ace1fbc75737ca60ceea7eeeef",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.4.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "React\\Promise\\": "src/"
+                },
+                "files": [
+                    "src/functions_include.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@googlemail.com"
+                }
+            ],
+            "description": "A lightweight implementation of CommonJS Promises/A for PHP",
+            "time": "2014-12-30 13:32:42"
+        }
+    ],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": {
+        "amphp/amp": 10
+    },
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": [],
+    "platform-dev": []
+}
index d91d9c7c0d31078436bf42b87ad771daf6c85730..6fcb7e3578a9f4ad03631b47b8e66f3a0e807c4e 100644 (file)
@@ -36,7 +36,7 @@ class Reference implements \IteratorAggregate
         * @param array $ref
         */
        function __construct($ref) {
         * @param array $ref
         */
        function __construct($ref) {
-               $this->name = self::name($ref);
+               $this->name = self::name($ref["foreignColumns"], $ref["referencedColumns"]);
                $this->foreignTable = $ref["foreignTable"];
                $this->foreignColumns = $ref["foreignColumns"];
                $this->referencedTable = $ref["referencedTable"];
                $this->foreignTable = $ref["foreignTable"];
                $this->foreignColumns = $ref["foreignColumns"];
                $this->referencedTable = $ref["referencedTable"];
@@ -53,13 +53,14 @@ class Reference implements \IteratorAggregate
        
        /**
         * Compose an identifying name
        
        /**
         * Compose an identifying name
-        * @param array $ref
+        * @param array $foreignColumns
+        * @param array $referencedColumns
         * @return string
         */
         * @return string
         */
-       static function name($ref) {
+       static function name(array $foreignColumns, array $referencedColumns) {
                return implode("_", array_map(function($ck, $cr) {
                        return preg_replace("/_$cr\$/", "", $ck);
                return implode("_", array_map(function($ck, $cr) {
                        return preg_replace("/_$cr\$/", "", $ck);
-               }, $ref["foreignColumns"], $ref["referencedColumns"]));
+               }, $foreignColumns, $referencedColumns));
        }
        
        /**
        }
        
        /**
diff --git a/lib/pq/Mapper/Map.php b/lib/pq/Mapper/Map.php
new file mode 100644 (file)
index 0000000..834ffb1
--- /dev/null
@@ -0,0 +1,198 @@
+<?php
+
+namespace pq\Mapper;
+
+use pq\Gateway\Row;
+use pq\Gateway\Rowset;
+use pq\Gateway\Table;
+use pq\Query\Expr;
+
+class Map implements MapInterface
+{
+       private $class;
+       private $gateway;
+       private $objects;
+       private $properties;
+
+       function __construct($class, Table $gateway, PropertyInterface ...$properties) {
+               $this->class = $class;
+               $this->gateway = $gateway;
+               $this->properties = $properties;
+               foreach ($properties as $property) {
+                       $property->setContainer($this);
+               }
+               $this->objects = new ObjectCache($this);
+       }
+
+       function getClass() {
+               return $this->class;
+       }
+
+       function getObjects() {
+               return $this->objects;
+       }
+
+       /**
+        * @return Table
+        */
+       function getGateway() {
+               return $this->gateway;
+       }
+
+       function getProperties() {
+               return $this->properties;
+       }
+
+       function addProperty(PropertyInterface $property) {
+               $property->setContainer($this);
+               $this->properties[] = $property;
+               return $this;
+       }
+/*
+       function idOf(Row $row, $check = false) {
+               $identity = $row->getIdentity();
+               if (is_scalar($identity)) {
+                       return $identity;
+               }
+
+               if ($check && !isset($identity)) {
+                       return false;
+               }
+
+               if (is_array($identity)) {
+                       if ($check && array_search(null, $identity, true)) {
+                               return false;
+                       }
+                       /* one level is better than no level * /
+                       asort($identity);
+               }
+               return json_encode($identity);
+       }
+       
+       function objectOf(Row $row) {
+               $id = $this->idOf($row);
+
+               if (isset($this->objects["obj"][$id])) {
+                       $obj = $this->objects["obj"][$id];
+               } else {
+                       $obj = new $this->class;
+                       $this->objects["obj"][$id] = $obj;
+                       $this->objects["row"][spl_object_hash($obj)] = $row;
+               }
+               return $obj;
+       }
+
+       function rowOf($object) {
+               $id = spl_object_hash($object);
+
+               if (isset($this->objects["row"][$id])) {
+                       $row = $this->objects["row"][$id];
+               } else {
+                       $row = new Row($this->gateway);
+                       $this->objects["row"][$id] = $row;
+               }
+               return $row;
+       }
+*/
+       function allOf(Row $row, $refName, &$objects = null) {
+               /* apply objectOf to populate the object cache */
+               return $this->gateway->of($row, $refName)->apply(function($row) use(&$objects) {
+                       $objects[] = $this->objects->asObject($row);
+               });
+       }
+
+       function refOf(Row $row, $refName, &$objects = null) {
+               $rid = [];
+               $rel = $row->getTable()->getRelation($this->gateway->getName(), $refName);
+               // FIXME: check if foreign key points to primary key
+               foreach ($rel as $fgn => $col) {
+                       $rid[$col] = $row->$fgn->get();
+               }
+               $rid = $this->objects->serializeRowId($rid);
+               if ($this->objects->hasObject($rid)) {
+                       $object = $this->objects->getObjectById($rid);
+                       $row = $this->objects->getRow($object);
+                       $objects[] = $object;
+                       $rowset = new Rowset($this->gateway);
+                       return $rowset->append($row);
+               }
+               /* apply objectOf to populate the object cache */
+               return $this->gateway->by($row, $refName)->apply(function($row) use(&$objects) {
+                       $objects[] = $this->objects->asObject($row);
+               });
+       }
+
+       function relOf(MapInterface $map, $refName) {
+               return $map->getGateway()->getRelation(
+                       $this->gateway->getName(), $refName);
+       }
+
+       private function drain(array $deferred, callable $exec) {
+               while ($deferred) {
+                       $cb = array_shift($deferred);
+                       if (($cb = $exec($cb))) {
+                               $deferred[] = $cb;
+                       }
+               }
+       }
+
+       function map(Row $row) {
+               $deferred = [];
+               $object = $this->objects->asObject($row);
+               foreach ($this->properties as $property) {
+                       if (($cb = $property->read($row, $object))) {
+                               $deferred[] = $cb;
+                       }
+               }
+               $this->drain($deferred, function(callable $cb) use($row, $object) {
+                       return $cb($row, $object);
+               });
+               return $object;
+       }
+
+       function mapAll(Rowset $rows) {
+               $objects = [];
+               foreach ($rows as $row) {
+                       $objects[] = $this->map($row);
+               }
+               return $objects;
+       }
+
+       function unmap($object) {
+               $deferred = [];
+               /* @var $row Row */
+               $row = $this->objects->asRow($object);
+               $upd = $this->objects->rowId($row, true);
+               foreach ($this->properties as $property) {
+                       if (($cb = $property->write($object, $row))) {
+                               $deferred[] = $cb;
+                       }
+               }
+               foreach ($this->gateway->getIdentity() as $col) {
+                       if (null === $row->$col->get()
+                       || ($row->$col->isExpr() && $row->$col->get()->isNull()))
+                       {
+                               $row->$col = new Expr("DEFAULT");
+                       }
+               }
+               if ($row->isDirty()) {
+                       if ($upd) {
+                               $row->update();
+                       } else {
+                               $row->create();
+                       }
+               }
+               foreach ($this->properties as $property) {
+                       if (($cb = $property->read($row, $object))) {
+                               $deferred[] = $cb;
+                       }
+               }
+               $this->drain($deferred, function($cb) use($object, $row) {
+                       return $cb($object, $row);
+               });
+               if ($row->isDirty()) {
+                       $row->update();
+               }
+       }
+
+}
\ No newline at end of file
diff --git a/lib/pq/Mapper/MapInterface.php b/lib/pq/Mapper/MapInterface.php
new file mode 100644 (file)
index 0000000..0c3430c
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+
+namespace pq\Mapper;
+
+use pq\Gateway\Row;
+use pq\Gateway\Rowset;
+use pq\Gateway\Table;
+
+interface MapInterface
+{
+       /**
+        * @return string
+        */
+       function getClass();
+
+       /**
+        * @return Table
+        */
+       function getGateway();
+
+       /**
+        * @return array of PropertyInterface instances
+        */
+       function getProperties();
+
+       /**
+        * @param PropertyInterface $property
+        */
+       function addProperty(PropertyInterface $property);
+
+       /**
+        * @param Row $row
+        * @return object
+        */
+       function map(Row $row);
+
+       /**
+        * @param \pq\Mapper\Rowset $rows
+        * @return array
+        */
+       function mapAll(Rowset $rows);
+
+       /**
+        * @param object $object
+        * @return Row
+        */
+       function unmap($object);
+}
\ No newline at end of file
diff --git a/lib/pq/Mapper/Mapper.php b/lib/pq/Mapper/Mapper.php
new file mode 100644 (file)
index 0000000..725a50c
--- /dev/null
@@ -0,0 +1,83 @@
+<?php
+
+namespace pq\Mapper;
+
+use UnexpectedValueException;
+
+class Mapper
+{
+       private $maps;
+       private $refp;
+
+       /**
+        * @param \pq\Mapper\MapInterface $map
+        * @return \pq\Mapper\Mapper
+        */
+       function register(MapInterface $map) {
+               $this->maps[$map->getClass()] = $map;
+               return $this;
+       }
+
+       function getReflector($class, $prop) {
+               if (is_object($class)) {
+                       $class = get_class($class);
+               }
+               $hash = "$class::$prop";
+               if (!isset($this->refp[$hash])) {
+                       $this->refp[$hash] = new \ReflectionProperty($class, $prop);
+                       $this->refp[$hash]->setAccessible(true);
+               }
+               return $this->refp[$hash];
+       }
+
+       /**
+        * @param string $class
+        * @return \pq\Mapper\MapInterface
+        * @throws UnexpectedValueException
+        */
+       function mapOf($class) {
+               if (is_object($class)) {
+                       $class = get_class($class);
+               }
+               if (!isset($this->maps[$class])) {
+                       if (!is_callable([$class, "mapAs"])) {
+                               throw new UnexpectedValueException("Not a mapped class: '$class'");
+                       }
+                       $this->register($class::mapAs($this));
+               }
+               return $this->maps[$class];
+       }
+
+       /**
+        * @param string $class
+        * @return \pq\Mapper\Storage
+        */
+       function createStorage($class) {
+               return new Storage($this, $class);
+       }
+
+       /**
+        * @param string $property
+        * @param string $field
+        * @return \pq\Mapper\Property\Field
+        */
+       function mapField($property, $field = null) {
+               return new Property\Field($this, $property, $field);
+       }
+
+       /**
+        * @param string $property
+        * @return \pq\Mapper\Property\All
+        */
+       function mapAll($property) {
+               return new Property\All($this, $property);
+       }
+
+       /**
+        * @param string $property
+        * @return \pq\Mapper\Property\Ref
+        */
+       function mapRef($property) {
+               return new Property\Ref($this, $property);
+       }
+}
diff --git a/lib/pq/Mapper/ObjectCache.php b/lib/pq/Mapper/ObjectCache.php
new file mode 100644 (file)
index 0000000..777dd51
--- /dev/null
@@ -0,0 +1,133 @@
+<?php
+
+namespace pq\Mapper;
+
+use OutOfBoundsException;
+use pq\Exception\BadMethodCallException;
+use pq\Gateway\Row;
+
+class ObjectCache
+{
+       private $map;
+       private $obj = [];
+       private $row = [];
+
+       function __construct(MapInterface $map) {
+               $this->map = $map;
+       }
+
+       function reset() {
+               $this->obj = [];
+               $this->row = [];
+       }
+
+       function rowId(Row $row, $check = false) {
+               try {
+                       $identity = $row->getIdentity();
+               } catch (OutOfBoundsException $e) {
+                       return false;
+               }
+               return $this->serializeRowId($identity, $check);
+       }
+
+       function objectId($object) {
+               return spl_object_hash($object);
+       }
+
+       function extractRowId($object) {
+               $id = [];
+               foreach ($this->map->getGateway()->getIdentity() as $col) {
+                       foreach ($this->map->getProperties() as $property) {
+                               if ($property->exposes($col)) {
+                                       $id[$col] = $property->extract($object);
+                               }
+                       }
+               }
+               return $this->serializeRowId($id, true);
+       }
+
+       function serializeRowId($identity, $check = false) {
+               if (is_scalar($identity)) {
+                       return $identity;
+               }
+
+               if ($check && !isset($identity)) {
+                       return false;
+               }
+
+               if (is_array($identity)) {
+                       if ($check && array_search(null, $identity, true)) {
+                               return false;
+                       }
+                       /* one level is better than no level */
+                       asort($identity);
+               }
+               return json_encode($identity);
+       }
+
+       function hasObject($row_id) {
+               return isset($this->obj[$row_id]);
+       }
+
+       function createObject(Row $row) {
+               $rid = $this->rowId($row);
+               $cls = $this->map->getClass();
+               $obj = new $cls;
+               $oid = $this->objectId($obj);
+               $this->obj[$rid] = $obj;
+               $this->row[$oid] = $row;
+               return $obj;
+       }
+
+       function resetObject(Row $row) {
+               unset($this->obj[$this->rowId($row)]);
+       }
+
+       function getObject(Row $row) {
+               $id = $this->rowId($row);
+               return $this->getObjectById($id);
+       }
+
+       function getObjectById($row_id) {
+               if (!$this->hasObject($row_id)) {
+                       throw new BadMethodCallException("Object of row with id $row_id does not exist");
+               }
+               return $this->obj[$row_id];
+       }
+
+       function asObject(Row $row){
+               return $this->hasObject($this->rowId($row))
+                       ? $this->getObject($row)
+                       : $this->createObject($row);
+       }
+
+       function hasRow($obj_id) {
+               return isset($this->row[$obj_id]);
+       }
+
+       function createRow($object) {
+               $oid = $this->objectId($object);
+               $row = new Row($this->map->getGateway());
+               $this->row[$oid] = $row;
+               return $row;
+       }
+
+       function resetRow($object) {
+               unset($this->row [$this->objectId($object)]);
+       }
+       
+       function getRow($object) {
+               $id = $this->objectId($object);
+
+               if (!$this->hasRow($id)) {
+                       throw new BadMethodCallException("Row for object with id $id does not exist");
+               }
+               return $this->row[$id];
+       }
+
+       function asRow($object) {
+               return $this->hasRow($this->objectId($object))
+                       ? $this->getRow($object)
+                       : $this->createRow($object);
+       }
+}
\ No newline at end of file
diff --git a/lib/pq/Mapper/Property.php b/lib/pq/Mapper/Property.php
new file mode 100644 (file)
index 0000000..ba4495b
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+namespace pq\Mapper;
+
+trait Property
+{
+       private $mapper;
+       private $field;
+       private $property;
+
+       function setContainer(MapInterface $container) {
+               $this->container = $container;
+       }
+
+       function getContainer() {
+               return $this->container;
+       }
+
+       function getProperty() {
+               return $this->property;
+       }
+
+       function defines($property) {
+               return $this->property === $property;
+       }
+
+       function exposes($field) {
+               return $this->field === $field;
+       }
+
+       function assign($object, $value) {
+               $this->mapper
+                       ->getReflector($object, $this->property)
+                       ->setValue($object, $value);
+       }
+
+       function extract($object) {
+               return $this->mapper
+                       ->getReflector($object, $this->property)
+                       ->getValue($object);
+       }
+
+       function __toString() {
+               return sprintf("%s: %s(%s)", get_class($this), $this->property, $this->field?:"NULL");
+       }
+}
\ No newline at end of file
diff --git a/lib/pq/Mapper/Property/All.php b/lib/pq/Mapper/Property/All.php
new file mode 100644 (file)
index 0000000..b53efdd
--- /dev/null
@@ -0,0 +1,95 @@
+<?php
+
+namespace pq\Mapper\Property;
+
+use pq\Gateway\Row;
+use pq\Mapper\Mapper;
+use pq\Mapper\RefProperty;
+use pq\Mapper\RefPropertyInterface;
+use UnexpectedValueException;
+
+class All implements RefPropertyInterface
+{
+       use RefProperty;
+       
+       function __construct(Mapper $mapper, $property) {
+               $this->mapper = $mapper;
+               $this->property = $property;
+       }
+       
+       function read(Row $row, $objectToUpdate) {
+               $val = $this->extract($objectToUpdate);
+               if (!isset($val)) {
+                       $map = $this->mapper->mapOf($this->refClass);
+                       $all = $map->allOf($row, $this->refName, $objects);
+                       $this->assign($objectToUpdate, $objects);
+                       $map->mapAll($all);
+               }
+       }
+
+       function write($object, Row $rowToUpdate) {
+               $property = $this->findRefProperty($object);
+               $map = $this->mapper->mapOf($this->refClass);
+               $refs = $this->extract($object);
+               foreach ($refs as $ref) {
+                       $property->assign($ref, $object);
+               }
+               return function() use($map, $refs) {
+                       foreach ($refs as $ref) {
+                               $map->unmap($ref);
+                       }
+               };
+               
+               if (!$this->container->getObjects()->rowId($rowToUpdate, true)) {
+                       return [$this, "write"];
+               } else {
+                       /* $object = User */
+                       /* $refs = array(Email) */
+                       /* $property = Property\Ref(Email::$user)->to(User)->by("email_user") */
+                       /* now update array(Email) with id of User, i.e. $ref->user_id = $object->id */
+                       $map = $this->mapper->mapOf($this->refClass);
+                       $refs = $this->extract($object);
+                       foreach ($refs as $ref) {
+                               $property->assign($ref, $object);
+                               $map->unmap($ref);
+                       }
+               }
+       }
+
+       private function findRefProperty($object) {
+               $map = $this->mapper->mapOf($this->refClass);
+               $property = array_filter($map->getProperties(), function($property) use($object) {
+                       if ($property instanceof RefPropertyInterface) {
+                               return $property->references($object) && $property->on($this->refName);
+                       }
+               });
+
+               if (1 != count($property)) {
+                       // FIXME: move the decl
+                       throw new UnexpectedValueException(
+                               sprintf("%s does not reference %s exactly once through %s",
+                                       $this->refClass, $this->container->getClass(), $this->refName));
+               }
+               return current($property);
+       }
+
+       function read2(RowGateway $row) {
+               #echo __METHOD__." ".$this;
+               $ref = $this->getRefMap()->ref($row, $this->refName);
+               $value = $this->mapper->map($ref, $this->refClass);
+               return [$this->property => $value];
+       }
+       
+       function write2($object) {
+               #echo __METHOD__." ".$this;
+               $value = $this->extract($object);
+               foreach ($value as $ref) {
+                       $this->mapper->queue(function() use(&$object, &$ref) {
+                               $map = $this->getRefMap()->getRefMapping($this->refName);
+                               $map->assign($ref, $object);
+                               $this->mapper->unmap($ref, $this->getRefMap());
+                       });
+               }
+               return [];
+       }
+}
\ No newline at end of file
diff --git a/lib/pq/Mapper/Property/Field.php b/lib/pq/Mapper/Property/Field.php
new file mode 100644 (file)
index 0000000..7a57741
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+
+namespace pq\Mapper\Property;
+
+use pq\Gateway\Row;
+
+use pq\Mapper\Mapper;
+use pq\Mapper\Property;
+use pq\Mapper\PropertyInterface;
+
+class Field implements PropertyInterface
+{
+       use Property;
+
+       function __construct(Mapper $mapper, $property, $field = null) {
+               $this->mapper = $mapper;
+               $this->property = $property;
+               $this->field = $field ?: $property;
+       }
+
+       function read(Row $row, $objectToUpdate) {
+               /* @var $val \pq\Gateway\Cell */
+               $val = $row->{$this->field};
+               $this->assign($objectToUpdate, $val->get());
+       }
+
+       function write($object, Row $rowToUpdate) {
+               $val = $this->extract($object);
+               $rowToUpdate->{$this->field} = $val;
+       }
+}
\ No newline at end of file
diff --git a/lib/pq/Mapper/Property/Ref.php b/lib/pq/Mapper/Property/Ref.php
new file mode 100644 (file)
index 0000000..851df41
--- /dev/null
@@ -0,0 +1,75 @@
+<?php
+
+namespace pq\Mapper\Property;
+
+use pq\Gateway\Row;
+use pq\Mapper\Mapper;
+use pq\Mapper\RefProperty;
+use pq\Mapper\RefPropertyInterface;
+
+class Ref implements RefPropertyInterface
+{
+       use RefProperty;
+       
+       function __construct(Mapper $mapper, $property) {
+               $this->mapper = $mapper;
+               $this->property = $property;
+       }
+
+       function read(Row $row, $objectToUpdate) {
+               $val = $this->extract($objectToUpdate);
+               if (!isset($val)) {
+                       $map = $this->mapper->mapOf($this->refClass);
+                       $ref = $map->refOf($row, $this->refName, $objects)->current();
+                       $this->assign($objectToUpdate, current($objects));
+                       $map->map($ref);
+               }
+       }
+
+       function write($object, Row $rowToUpdate) {
+               $map = $this->mapper->mapOf($this->refClass);
+               $ref = $this->extract($object);
+               $rel = $map->relOf($this->container, $this->refName);
+               foreach ($rel as $fgn => $col) {
+                       foreach ($this->findFieldProperty($col) as $property) {
+                               $value = $property->extract($ref);
+                               $rowToUpdate->$fgn = $value;
+                       }
+               }
+       }
+
+       private function findFieldProperty($col) {
+               $map = $this->mapper->mapOf($this->refClass);
+               return array_filter($map->getProperties(), function($property) use($col) {
+                       return $property->exposes($col);
+               });
+       }
+
+
+       function read2(RowGateway $row) {
+               #echo __METHOD__." ".$this;
+               $map = $this->getRefMap();
+               $rel = $this->container->getGateway()->getRelation(
+                       $map->getGateway()->getName(), $this->refName);
+               $key = array_combine($rel->referencedColumns, array_map(function($c) use($row) {
+                       return $row->$c->get();
+               }, $rel->foreignColumns));
+               if (($obj = $this->mapper->objectOfRowId($this->refClass, $key))) {
+                       yield $this->property => $obj;
+               } else foreach ($map->getGateway()->by($row, $this->refName) as $row) {
+                       yield $this->property => $this->mapper->objectOf($this->refClass, $row);
+               }
+       }
+       
+       function write2($object) {
+               #echo __METHOD__." ".$this;
+               $map = $this->getRefMap();
+               $rel = $this->container->getGateway()->getRelation(
+                       $map->getGateway()->getName(), $this->refName);
+               $ref = $this->extract($object);
+               foreach ($rel as $fgn => $col) {
+                       $fld = $map->getFieldMapping($col);
+                       yield $fgn => $fld->extract($ref);
+               }
+       }
+} 
\ No newline at end of file
diff --git a/lib/pq/Mapper/PropertyInterface.php b/lib/pq/Mapper/PropertyInterface.php
new file mode 100644 (file)
index 0000000..da6ccd3
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+
+namespace pq\Mapper;
+
+use pq\Gateway\Row;
+
+interface PropertyInterface
+{
+       function write($object, Row $rowToUpdate);
+       function read(Row $row, $objectToUpdate);
+
+       function assign($object, $value);
+       function extract($object);
+       
+       function getProperty();
+
+       function getContainer();
+       function setContainer(MapInterface $container);
+       
+       function defines($property);
+       function exposes($field);
+
+}
diff --git a/lib/pq/Mapper/RefProperty.php b/lib/pq/Mapper/RefProperty.php
new file mode 100644 (file)
index 0000000..d7c6c6f
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+
+namespace pq\Mapper;
+
+trait RefProperty
+{
+       use Property;
+
+       private $refClass;
+       private $refName;
+
+       function to($class) {
+               $this->refClass = $class;
+               return $this;
+       }
+
+       function references($class) {
+               return $this->refClass === (is_object($class) ? get_class($class) : $class);
+       }
+
+       function by($ref) {
+               $this->refName = $ref;
+               return $this;
+       }
+
+       function on($ref) {
+               return $this->refName === $ref;
+       }
+
+}
diff --git a/lib/pq/Mapper/RefPropertyInterface.php b/lib/pq/Mapper/RefPropertyInterface.php
new file mode 100644 (file)
index 0000000..5fae13a
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+namespace pq\Mapper;
+
+interface RefPropertyInterface extends PropertyInterface
+{
+       /**
+        * @param string $class
+        */
+       function to($class);
+
+       /**
+        * @param string $class
+        * @return bool
+        */
+       function references($class);
+
+       /**
+        * @param string $ref
+        */
+       function by($ref);
+
+       /**
+        * @param string $ref
+        * @return bool
+        */
+       function on($ref);
+}
diff --git a/lib/pq/Mapper/Storage.php b/lib/pq/Mapper/Storage.php
new file mode 100644 (file)
index 0000000..a27ea68
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+
+namespace pq\Mapper;
+
+class Storage implements StorageInterface
+{
+       /**
+        * @var string
+        */
+       private $class;
+
+       /**
+        * @var \pq\Mapper\Mapper
+        */
+       private $mapper;
+
+       /**
+        *
+        * @var pq\Mapper\MapInterface
+        */
+       private $mapping;
+
+       /**
+        * @var \pq\Gateway\Table
+        */
+       private $gateway;
+       
+       function __construct(Mapper $mapper, $class) {
+               $this->class = $class;
+               $this->mapper = $mapper;
+               $this->mapping = $mapper->mapOf($class);
+               $this->gateway = $this->mapping->getGateway();
+       }
+       
+       function getMapper() {
+               return $this->mapper;
+       }
+       
+       function find($where = [], $order = null, $limit = null, $offset = null) {
+               /* @var pq\Gateway\Rowset $rowset */
+               $rowset = $this->gateway->find($where, $order, $limit, $offset);
+               return $this->mapping->mapAll($rowset);
+       }
+       
+       function delete($object) {
+               $cache = $this->mapping->getObjects();
+               $row = $cache->asRow($object)->delete();
+               $cache->resetObject($row);
+               $cache->resetRow($object);
+       }
+       
+       function persist($object) {
+               $this->mapping->unmap($object);
+       }
+       
+}
\ No newline at end of file
diff --git a/lib/pq/Mapper/StorageInterface.php b/lib/pq/Mapper/StorageInterface.php
new file mode 100644 (file)
index 0000000..db16df5
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+
+namespace pq\Mapper;
+
+interface StorageInterface
+{
+       /**
+        * @return pq\Mapper\Mapper
+        */
+       function getMapper();
+       
+       function find($where, $order = null, $limit = null, $offset = null);
+       function delete($object);
+       function persist($object);
+}
\ No newline at end of file
diff --git a/pq-stub.php b/pq-stub.php
new file mode 100644 (file)
index 0000000..11830c4
--- /dev/null
@@ -0,0 +1,620 @@
+<?php
+
+namespace pq 
+{
+
+       interface Converter 
+       {
+               abstract public function convertTypes();
+
+               abstract public function convertFromString($data, $type);
+
+               abstract public function convertToString($data, $type);
+
+       }
+
+       class Cancel 
+       {
+               public $connection;
+
+               public function __construct(\pq\Connection $connection) {
+               }
+
+               public function cancel() {
+               }
+
+       }
+
+       interface Exception 
+       {
+               const INVALID_ARGUMENT = 0;
+               const RUNTIME = 1;
+               const CONNECTION_FAILED = 2;
+               const IO = 3;
+               const ESCAPE = 4;
+               const BAD_METHODCALL = 5;
+               const UNINITIALIZED = 6;
+               const DOMAIN = 7;
+               const SQL = 8;
+
+       }
+
+       class COPY 
+       {
+               const FROM_STDIN = 0;
+               const TO_STDOUT = 1;
+
+               public $connection;
+               public $expression;
+               public $direction;
+               public $options;
+
+               public function __construct(\pq\Connection $connection, $expression, $direction, $options = NULL) {
+               }
+
+               public function put($data) {
+               }
+
+               public function end($error = NULL) {
+               }
+
+               public function get(&$data) {
+               }
+
+       }
+
+       class Connection 
+       {
+               const OK = 0;
+               const BAD = 1;
+               const STARTED = 2;
+               const MADE = 3;
+               const AWAITING_RESPONSE = 4;
+               const AUTH_OK = 5;
+               const SSL_STARTUP = 7;
+               const SETENV = 6;
+               const TRANS_IDLE = 0;
+               const TRANS_ACTIVE = 1;
+               const TRANS_INTRANS = 2;
+               const TRANS_INERROR = 3;
+               const TRANS_UNKNOWN = 4;
+               const POLLING_FAILED = 0;
+               const POLLING_READING = 1;
+               const POLLING_WRITING = 2;
+               const POLLING_OK = 3;
+               const EVENT_NOTICE = 'notice';
+               const EVENT_RESULT = 'result';
+               const EVENT_RESET = 'reset';
+               const ASYNC = 1;
+               const PERSISTENT = 2;
+
+               public $status;
+               public $transactionStatus;
+               public $socket;
+               public $errorMessage;
+               public $busy;
+               public $encoding;
+               public $unbuffered;
+               public $db;
+               public $user;
+               public $pass;
+               public $host;
+               public $port;
+               public $params;
+               public $options;
+               public $eventHandlers;
+               public $defaultFetchType;
+               public $defaultTransactionIsolation;
+               public $defaultTransactionReadonly;
+               public $defaultTransactionDeferrable;
+               public $defaultAutoConvert;
+
+               public function __construct($dsn, $async = NULL) {
+               }
+
+               public function reset() {
+               }
+
+               public function resetAsync() {
+               }
+
+               public function poll() {
+               }
+
+               public function exec($query) {
+               }
+
+               public function execAsync($query, $callable = NULL) {
+               }
+
+               public function execParams($query, array $params, array $types = NULL) {
+               }
+
+               public function execParamsAsync($query, array $params, array $types = NULL, $callable = NULL) {
+               }
+
+               public function prepare($name, $query, array $types = NULL) {
+               }
+
+               public function prepareAsync($name, $query, array $types = NULL) {
+               }
+
+               public function declare($name, $flags, $query) {
+               }
+
+               public function declareAsync($name, $flags, $query = NULL) {
+               }
+
+               public function unlisten($channel) {
+               }
+
+               public function unlistenAsync($channel) {
+               }
+
+               public function listen($channel, $callable) {
+               }
+
+               public function listenAsync($channel = NULL, $callable = NULL) {
+               }
+
+               public function notify($channel, $message) {
+               }
+
+               public function notifyAsync($channel, $message) {
+               }
+
+               public function getResult() {
+               }
+
+               public function quote($string) {
+               }
+
+               public function quoteName($type) {
+               }
+
+               public function escapeBytea($bytea) {
+               }
+
+               public function unescapeBytea($bytea) {
+               }
+
+               public function startTransaction($isolation = NULL, $readonly = NULL, $deferrable = NULL) {
+               }
+
+               public function startTransactionAsync($isolation = NULL, $readonly = NULL, $deferrable = NULL) {
+               }
+
+               public function trace($stdio_stream = NULL) {
+               }
+
+               public function off($type) {
+               }
+
+               public function on($type, $callable) {
+               }
+
+               public function setConverter(\pq\Converter $converter) {
+               }
+
+               public function unsetConverter(\pq\Converter $converter) {
+               }
+
+       }
+
+       class Cursor 
+       {
+               const BINARY = 1;
+               const INSENSITIVE = 2;
+               const WITH_HOLD = 4;
+               const SCROLL = 16;
+               const NO_SCROLL = 32;
+
+               public $name;
+               public $connection;
+
+               public function __construct(\pq\Connection $connection, $name, $flags, $query, $async = NULL) {
+               }
+
+               public function open() {
+               }
+
+               public function close() {
+               }
+
+               public function fetch($spec) {
+               }
+
+               public function move($spec = NULL) {
+               }
+
+               public function fetchAsync($spec = NULL, $callback = NULL) {
+               }
+
+               public function moveAsync($spec = NULL, $callback = NULL) {
+               }
+
+       }
+
+       class DateTime extends \DateTime implements \DateTimeInterface, \JsonSerializable 
+       {
+               public $format;
+
+               public function __toString() {
+               }
+
+               public function jsonSerialize() {
+               }
+
+       }
+
+       class LOB 
+       {
+               const INVALID_OID = 0;
+               const R = 262144;
+               const W = 131072;
+               const RW = 393216;
+
+               public $transaction;
+               public $oid;
+               public $stream;
+
+               public function __construct(\pq\Transaction $transaction, $oid = NULL, $mode = NULL) {
+               }
+
+               public function write($data) {
+               }
+
+               public function read($length = NULL, &$read = NULL) {
+               }
+
+               public function seek($offset, $whence = NULL) {
+               }
+
+               public function tell() {
+               }
+
+               public function truncate($length = NULL) {
+               }
+
+       }
+
+       class Result implements \Countable 
+       {
+               const EMPTY_QUERY = 0;
+               const COMMAND_OK = 1;
+               const TUPLES_OK = 2;
+               const COPY_OUT = 3;
+               const COPY_IN = 4;
+               const BAD_RESPONSE = 5;
+               const NONFATAL_ERROR = 6;
+               const FATAL_ERROR = 7;
+               const COPY_BOTH = 8;
+               const SINGLE_TUPLE = 9;
+               const FETCH_ARRAY = 0;
+               const FETCH_ASSOC = 1;
+               const FETCH_OBJECT = 2;
+               const CONV_BOOL = 1;
+               const CONV_INT = 2;
+               const CONV_FLOAT = 4;
+               const CONV_SCALAR = 15;
+               const CONV_ARRAY = 16;
+               const CONV_DATETIME = 32;
+               const CONV_JSON = 256;
+               const CONV_ALL = 65535;
+
+               public $status;
+               public $statusMessage;
+               public $errorMessage;
+               public $numRows;
+               public $numCols;
+               public $affectedRows;
+               public $fetchType;
+               public $autoConvert;
+
+               public function bind($col, &$ref) {
+               }
+
+               public function fetchBound() {
+               }
+
+               public function fetchRow($fetch_type = NULL) {
+               }
+
+               public function fetchCol(&$ref, $col = NULL) {
+               }
+
+               public function fetchAll($fetch_type = NULL) {
+               }
+
+               public function fetchAllCols($col = NULL) {
+               }
+
+               public function count() {
+               }
+
+               public function map($keys = NULL, $vals = NULL, $fetch_type = NULL) {
+               }
+
+               public function desc() {
+               }
+
+       }
+
+       class Statement 
+       {
+               public $name;
+               public $connection;
+
+               public function __construct(\pq\Connection $connection, $name, $query, array $types = NULL, $async = NULL) {
+               }
+
+               public function bind($param_no, &$param_ref) {
+               }
+
+               public function exec(array $params = NULL) {
+               }
+
+               public function desc() {
+               }
+
+               public function execAsync(array $params = NULL, $callable = NULL) {
+               }
+
+               public function descAsync($callable) {
+               }
+
+       }
+
+       class Transaction 
+       {
+               const READ_COMMITTED = 0;
+               const REPEATABLE_READ = 1;
+               const SERIALIZABLE = 2;
+
+               public $connection;
+               public $isolation;
+               public $readonly;
+               public $deferrable;
+
+               public function __construct(\pq\Connection $connection, $async = NULL, $isolation = NULL, $readonly = NULL, $deferrable = NULL) {
+               }
+
+               public function commit() {
+               }
+
+               public function rollback() {
+               }
+
+               public function commitAsync() {
+               }
+
+               public function rollbackAsync() {
+               }
+
+               public function savepoint() {
+               }
+
+               public function savepointAsync() {
+               }
+
+               public function exportSnapshot() {
+               }
+
+               public function exportSnapshotAsync() {
+               }
+
+               public function importSnapshot($snapshot_id) {
+               }
+
+               public function importSnapshotAsync($snapshot_id) {
+               }
+
+               public function openLOB($oid, $mode = NULL) {
+               }
+
+               public function createLOB($mode = NULL) {
+               }
+
+               public function unlinkLOB($oid) {
+               }
+
+               public function importLOB($local_path, $oid = NULL) {
+               }
+
+               public function exportLOB($oid, $local_path) {
+               }
+
+       }
+
+       class Types 
+       {
+               const BOOL = 16;
+               const BYTEA = 17;
+               const CHAR = 18;
+               const NAME = 19;
+               const INT8 = 20;
+               const INT2 = 21;
+               const INT2VECTOR = 22;
+               const INT4 = 23;
+               const REGPROC = 24;
+               const TEXT = 25;
+               const OID = 26;
+               const TID = 27;
+               const XID = 28;
+               const CID = 29;
+               const OIDVECTOR = 30;
+               const PG_TYPE = 71;
+               const PG_ATTRIBUTE = 75;
+               const PG_PROC = 81;
+               const PG_CLASS = 83;
+               const JSON = 114;
+               const XML = 142;
+               const XMLARRAY = 143;
+               const JSONARRAY = 199;
+               const PG_NODE_TREE = 194;
+               const SMGR = 210;
+               const POINT = 600;
+               const LSEG = 601;
+               const PATH = 602;
+               const BOX = 603;
+               const POLYGON = 604;
+               const LINE = 628;
+               const LINEARRAY = 629;
+               const FLOAT4 = 700;
+               const FLOAT8 = 701;
+               const ABSTIME = 702;
+               const RELTIME = 703;
+               const TINTERVAL = 704;
+               const UNKNOWN = 705;
+               const CIRCLE = 718;
+               const CIRCLEARRAY = 719;
+               const MONEY = 790;
+               const MONEYARRAY = 791;
+               const MACADDR = 829;
+               const INET = 869;
+               const CIDR = 650;
+               const BOOLARRAY = 1000;
+               const BYTEAARRAY = 1001;
+               const CHARARRAY = 1002;
+               const NAMEARRAY = 1003;
+               const INT2ARRAY = 1005;
+               const INT2VECTORARRAY = 1006;
+               const INT4ARRAY = 1007;
+               const REGPROCARRAY = 1008;
+               const TEXTARRAY = 1009;
+               const OIDARRAY = 1028;
+               const TIDARRAY = 1010;
+               const XIDARRAY = 1011;
+               const CIDARRAY = 1012;
+               const OIDVECTORARRAY = 1013;
+               const BPCHARARRAY = 1014;
+               const VARCHARARRAY = 1015;
+               const INT8ARRAY = 1016;
+               const POINTARRAY = 1017;
+               const LSEGARRAY = 1018;
+               const PATHARRAY = 1019;
+               const BOXARRAY = 1020;
+               const FLOAT4ARRAY = 1021;
+               const FLOAT8ARRAY = 1022;
+               const ABSTIMEARRAY = 1023;
+               const RELTIMEARRAY = 1024;
+               const TINTERVALARRAY = 1025;
+               const POLYGONARRAY = 1027;
+               const ACLITEM = 1033;
+               const ACLITEMARRAY = 1034;
+               const MACADDRARRAY = 1040;
+               const INETARRAY = 1041;
+               const CIDRARRAY = 651;
+               const CSTRINGARRAY = 1263;
+               const BPCHAR = 1042;
+               const VARCHAR = 1043;
+               const DATE = 1082;
+               const TIME = 1083;
+               const TIMESTAMP = 1114;
+               const TIMESTAMPARRAY = 1115;
+               const DATEARRAY = 1182;
+               const TIMEARRAY = 1183;
+               const TIMESTAMPTZ = 1184;
+               const TIMESTAMPTZARRAY = 1185;
+               const INTERVAL = 1186;
+               const INTERVALARRAY = 1187;
+               const NUMERICARRAY = 1231;
+               const TIMETZ = 1266;
+               const TIMETZARRAY = 1270;
+               const BIT = 1560;
+               const BITARRAY = 1561;
+               const VARBIT = 1562;
+               const VARBITARRAY = 1563;
+               const NUMERIC = 1700;
+               const REFCURSOR = 1790;
+               const REFCURSORARRAY = 2201;
+               const REGPROCEDURE = 2202;
+               const REGOPER = 2203;
+               const REGOPERATOR = 2204;
+               const REGCLASS = 2205;
+               const REGTYPE = 2206;
+               const REGPROCEDUREARRAY = 2207;
+               const REGOPERARRAY = 2208;
+               const REGOPERATORARRAY = 2209;
+               const REGCLASSARRAY = 2210;
+               const REGTYPEARRAY = 2211;
+               const UUID = 2950;
+               const UUIDARRAY = 2951;
+               const PG_LSN = 3220;
+               const PG_LSNARRAY = 3221;
+               const TSVECTOR = 3614;
+               const GTSVECTOR = 3642;
+               const TSQUERY = 3615;
+               const REGCONFIG = 3734;
+               const REGDICTIONARY = 3769;
+               const TSVECTORARRAY = 3643;
+               const GTSVECTORARRAY = 3644;
+               const TSQUERYARRAY = 3645;
+               const REGCONFIGARRAY = 3735;
+               const REGDICTIONARYARRAY = 3770;
+               const JSONB = 3802;
+               const JSONBARRAY = 3807;
+               const TXID_SNAPSHOT = 2970;
+               const TXID_SNAPSHOTARRAY = 2949;
+               const INT4RANGE = 3904;
+               const INT4RANGEARRAY = 3905;
+               const NUMRANGE = 3906;
+               const NUMRANGEARRAY = 3907;
+               const TSRANGE = 3908;
+               const TSRANGEARRAY = 3909;
+               const TSTZRANGE = 3910;
+               const TSTZRANGEARRAY = 3911;
+               const DATERANGE = 3912;
+               const DATERANGEARRAY = 3913;
+               const INT8RANGE = 3926;
+               const INT8RANGEARRAY = 3927;
+               const RECORD = 2249;
+               const RECORDARRAY = 2287;
+               const CSTRING = 2275;
+               const ANY = 2276;
+               const ANYARRAY = 2277;
+               const VOID = 2278;
+               const TRIGGER = 2279;
+               const EVENT_TRIGGER = 3838;
+               const LANGUAGE_HANDLER = 2280;
+               const INTERNAL = 2281;
+               const OPAQUE = 2282;
+               const ANYELEMENT = 2283;
+               const ANYNONARRAY = 2776;
+               const ANYENUM = 3500;
+               const FDW_HANDLER = 3115;
+               const ANYRANGE = 3831;
+
+               public $connection;
+
+               public function __construct(\pq\Connection $connection, array $namespaces = NULL) {
+               }
+
+               public function refresh(array $namespaces = NULL) {
+               }
+
+       }
+}
+
+namespace pq\Exception 
+{
+
+       class BadMethodCallException extends \BadMethodCallException implements \pq\Exception 
+       {
+       }
+
+       class InvalidArgumentException extends \InvalidArgumentException implements \pq\Exception 
+       {
+       }
+
+       class RuntimeException extends \RuntimeException implements \pq\Exception 
+       {
+       }
+
+       class DomainException extends \DomainException implements \pq\Exception 
+       {
+               public $sqlstate;
+
+       }
+}
+