#!/usr/bin/php -dphar.readonly=1 2, 'c' => 'text/plain', 'cc' => 'text/plain', 'cpp' => 'text/plain', 'c++' => 'text/plain', 'dtd' => 'text/plain', 'h' => 'text/plain', 'log' => 'text/plain', 'rng' => 'text/plain', 'txt' => 'text/plain', 'xsd' => 'text/plain', 'php' => 1, 'inc' => 1, 'avi' => 'video/avi', 'bmp' => 'image/bmp', 'css' => 'text/css', 'gif' => 'image/gif', 'htm' => 'text/html', 'html' => 'text/html', 'htmls' => 'text/html', 'ico' => 'image/x-ico', 'jpe' => 'image/jpeg', 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'js' => 'application/x-javascript', 'midi' => 'audio/midi', 'mid' => 'audio/midi', 'mod' => 'audio/mod', 'mov' => 'movie/quicktime', 'mp3' => 'audio/mp3', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'pdf' => 'application/pdf', 'png' => 'image/png', 'swf' => 'application/shockwave-flash', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'wav' => 'audio/wav', 'xbm' => 'image/xbm', 'xml' => 'text/xml', ); header("Cache-Control: no-cache, must-revalidate"); header("Pragma: no-cache"); $basename = basename(__FILE__); if (!strpos($_SERVER['REQUEST_URI'], $basename)) { chdir(Extract_Phar::$temp); include $web; return; } $pt = substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], $basename) + strlen($basename)); if (!$pt || $pt == '/') { $pt = $web; header('HTTP/1.1 301 Moved Permanently'); header('Location: ' . $_SERVER['REQUEST_URI'] . '/' . $pt); exit; } $a = realpath(Extract_Phar::$temp . DIRECTORY_SEPARATOR . $pt); if (!$a || strlen(dirname($a)) < strlen(Extract_Phar::$temp)) { header('HTTP/1.0 404 Not Found'); echo "\n \n File Not Found<title>\n </head>\n <body>\n <h1>404 - File ", $pt, " Not Found</h1>\n </body>\n</html>"; exit; } $b = pathinfo($a); if (!isset($b['extension'])) { header('Content-Type: text/plain'); header('Content-Length: ' . filesize($a)); readfile($a); exit; } if (isset($mimes[$b['extension']])) { if ($mimes[$b['extension']] === 1) { include $a; exit; } if ($mimes[$b['extension']] === 2) { highlight_file($a); exit; } header('Content-Type: ' .$mimes[$b['extension']]); header('Content-Length: ' . filesize($a)); readfile($a); exit; } } class Extract_Phar { static $temp; static $origdir; const GZ = 0x1000; const BZ2 = 0x2000; const MASK = 0x3000; const START = 'pharext_installer.php'; const LEN = 6697; static function go($return = false) { $fp = fopen(__FILE__, 'rb'); fseek($fp, self::LEN); $L = unpack('V', $a = (binary)fread($fp, 4)); $m = (binary)''; do { $read = 8192; if ($L[1] - strlen($m) < 8192) { $read = $L[1] - strlen($m); } $last = (binary)fread($fp, $read); $m .= $last; } while (strlen($last) && strlen($m) < $L[1]); if (strlen($m) < $L[1]) { die('ERROR: manifest length read was "' . strlen($m) .'" should be "' . $L[1] . '"'); } $info = self::_unpack($m); $f = $info['c']; if ($f & self::GZ) { if (!function_exists('gzinflate')) { die('Error: zlib extension is not enabled -' . ' gzinflate() function needed for zlib-compressed .phars'); } } if ($f & self::BZ2) { if (!function_exists('bzdecompress')) { die('Error: bzip2 extension is not enabled -' . ' bzdecompress() function needed for bz2-compressed .phars'); } } $temp = self::tmpdir(); if (!$temp || !is_writable($temp)) { $sessionpath = session_save_path(); if (strpos ($sessionpath, ";") !== false) $sessionpath = substr ($sessionpath, strpos ($sessionpath, ";")+1); if (!file_exists($sessionpath) || !is_dir($sessionpath)) { die('Could not locate temporary directory to extract phar'); } $temp = $sessionpath; } $temp .= '/pharextract/'.basename(__FILE__, '.phar'); self::$temp = $temp; self::$origdir = getcwd(); @mkdir($temp, 0777, true); $temp = realpath($temp); if (!file_exists($temp . DIRECTORY_SEPARATOR . md5_file(__FILE__))) { self::_removeTmpFiles($temp, getcwd()); @mkdir($temp, 0777, true); @file_put_contents($temp . '/' . md5_file(__FILE__), ''); foreach ($info['m'] as $path => $file) { $a = !file_exists(dirname($temp . '/' . $path)); @mkdir(dirname($temp . '/' . $path), 0777, true); clearstatcache(); if ($path[strlen($path) - 1] == '/') { @mkdir($temp . '/' . $path, 0777); } else { file_put_contents($temp . '/' . $path, self::extractFile($path, $file, $fp)); @chmod($temp . '/' . $path, 0666); } } } chdir($temp); if (!$return) { include self::START; } } static function tmpdir() { if (strpos(PHP_OS, 'WIN') !== false) { if ($var = getenv('TMP') ? getenv('TMP') : getenv('TEMP')) { return $var; } if (is_dir('/temp') || mkdir('/temp')) { return realpath('/temp'); } return false; } if ($var = getenv('TMPDIR')) { return $var; } return realpath('/tmp'); } static function _unpack($m) { $info = unpack('V', substr($m, 0, 4)); $l = unpack('V', substr($m, 10, 4)); $m = substr($m, 14 + $l[1]); $s = unpack('V', substr($m, 0, 4)); $o = 0; $start = 4 + $s[1]; $ret['c'] = 0; for ($i = 0; $i < $info[1]; $i++) { $len = unpack('V', substr($m, $start, 4)); $start += 4; $savepath = substr($m, $start, $len[1]); $start += $len[1]; $ret['m'][$savepath] = array_values(unpack('Va/Vb/Vc/Vd/Ve/Vf', substr($m, $start, 24))); $ret['m'][$savepath][3] = sprintf('%u', $ret['m'][$savepath][3] & 0xffffffff); $ret['m'][$savepath][7] = $o; $o += $ret['m'][$savepath][2]; $start += 24 + $ret['m'][$savepath][5]; $ret['c'] |= $ret['m'][$savepath][4] & self::MASK; } return $ret; } static function extractFile($path, $entry, $fp) { $data = ''; $c = $entry[2]; while ($c) { if ($c < 8192) { $data .= @fread($fp, $c); $c = 0; } else { $c -= 8192; $data .= @fread($fp, 8192); } } if ($entry[4] & self::GZ) { $data = gzinflate($data); } elseif ($entry[4] & self::BZ2) { $data = bzdecompress($data); } if (strlen($data) != $entry[0]) { die("Invalid internal .phar file (size error " . strlen($data) . " != " . $stat[7] . ")"); } if ($entry[3] != sprintf("%u", crc32((binary)$data) & 0xffffffff)) { die("Invalid internal .phar file (checksum error)"); } return $data; } static function _removeTmpFiles($temp, $origdir) { chdir($temp); foreach (glob('*') as $f) { if (file_exists($f)) { is_dir($f) ? @rmdir($f) : @unlink($f); if (file_exists($f) && is_dir($f)) { self::_removeTmpFiles($f, getcwd()); } } } @rmdir($temp); clearstatcache(); chdir($origdir); } } Extract_Phar::go(); __HALT_COMPILER(); ?> ��t�����������P��a:8:{s:6:"header";s:49:"pharext v3.0.1 (c) Michael Wallner <mike@php.net>";s:7:"version";s:5:"3.0.1";s:4:"name";s:2:"pq";s:4:"date";s:10:"2015-05-06";s:4:"stub";s:21:"pharext_installer.php";s:7:"license";s:1345:"Copyright (c) 2013, Michael Wallner <mike@php.net>. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ";s:7:"release";s:8:"0.6.0dev";s:4:"type";s:9:"extension";}���pharext/Cli/Args.phpK�� IUK��O���������pharext/Cli/Command.php�� IU�� *۶���������pharext/Command.php�� IU��td\���������pharext/Exception.phpc�� IUc��U{���������pharext/ExecCmd.php�� IU��lʶ���������pharext/Installer.php�� IU��WJ���������pharext/Openssl/PrivateKey.php�� IU��&P���������pharext/Packager.phpt!�� IUt!��;c���������pharext/SourceDir/Basic.php�� IU��zZ���������pharext/SourceDir/Git.php�� IU��bc���������pharext/SourceDir/Pecl.php�� IU�����������pharext/SourceDir.php]�� IU]��S ���������pharext/Task/Activate.phpp �� IUp ��EE���������pharext/Task/Askpass.phpU�� IUU��*������ ���pharext/Task/BundleGenerator.php}�� IU}�� `Y���������pharext/Task/Cleanup.php��� IU���ZͶ���������pharext/Task/Configure.phpT�� IUT��}���������pharext/Task/Extract.php�� IU��ն���������pharext/Task/GitClone.php�� IU��CR(���������pharext/Task/Make.php�� IU��6 ���������pharext/Task/PaxFixup.php�� IU��y���������pharext/Task/PeclFixup.php�� IU��et���������pharext/Task/PharBuild.php�� IU��D垶���������pharext/Task/PharCompress.phpr�� IUr�� ���������pharext/Task/PharRename.php�� IU��[˶���������pharext/Task/PharSign.php�� IU��ۺi���������pharext/Task/Phpize.php�� IU�� 2Ѷ���������pharext/Task/StreamFetch.php�� IU��s\���������pharext/Task.phpw��� IUw��� IǶ���������pharext/Tempdir.php�� IU��,���������pharext/Tempfile.php�� IU��#���������pharext/Tempname.php`�� IU`��<Np���������pharext/Version.php4��� IU4���p���������pharext_installer.php��� IU���pDZ���������pharext_packager.php��� IU���1���������pharext_package.php2��� IU2���vSTҶ������ ���package.xmlj�� IUj�� ���������CREDITS��� IU���u?U���������LICENSEA�� IUA��J������ ���config.m4?��� IU?���o.������ ���config9.m4�� IU��Ԋ���������php_pq.hz�� IUz��2y���������php_pq_type.awk%�� IU%��m,������ ���php_pq_type.hJ�� IUJ��H���������src/php_pq_callback.c �� IU ��!���������src/php_pq_callback.h}�� IU}��{���������src/php_pqcancel.c�� IU��B���������src/php_pqcancel.h�� IU��\���������src/php_pqconn.cK � IUK �W���������src/php_pqconn_event.c�� IU��g& ���������src/php_pqconn_event.hI�� IUI��Q`���������src/php_pqconn.h �� IU ��ݶ���������src/php_pqcopy.cE/�� IUE/��{���������src/php_pqcopy.h�� IU�� ���������src/php_pqcur.c0�� IU0������������src/php_pqcur.h�� IU��Ge@���������src/php_pqexc.c3�� IU3��͋���������src/php_pqexc.h�� IU��h���������src/php_pqlob.c@�� IU@��@���������src/php_pqlob.hq�� IUq��*K���������src/php_pq_misc.cq&�� IUq&��i���������src/php_pq_misc.h�� IU��}���������src/php_pq_module.c �� IU ��<o���������src/php_pq_object.c�� IU��J���������src/php_pq_object.h�� IU��w���������src/php_pq_params.cG'�� IUG'��{ն���������src/php_pq_params.hq�� IUq��J���������src/php_pqres.c@�� IU@��De���������src/php_pqres.h �� IU ��&#ݶ���������src/php_pqstm.c$H�� IU$H��’ȶ���������src/php_pqstm.h7�� IU7��7b���������src/php_pqtxn.cv�� IUv��>y���������src/php_pqtxn.h�� IU��#���������src/php_pqtypes.c,�� IU,��kus���������src/php_pqtypes.h}�� IU}��$@���������tests/async001.phptN�� IUN��mg���������tests/async002.phpt�� IU��0Fz׶���������tests/async003.phpt�� IU��hߏ���������tests/async004.phpt�� IU��[���������tests/async005.phpt�� IU��92b���������tests/async006.phpt|�� IU|��o'���������tests/async007.phpt�� IU��KU���������tests/async008.phptZ�� IUZ��`3���������tests/basic001.phptE�� IUE��u]X���������tests/basic002.phpt�� IU��Jն���������tests/bound002.phpt�� IU��U���������tests/cancel001.phpt�� IU�����������tests/conv001.phpt�� IU��BSc���������tests/copy001.phpt�� IU��g���������tests/cursor001.phpt�� IU�����������tests/encoding001.phpt�� IU��$b���������tests/exceptions001.phpt$�� IU$��w���������tests/exceptions002.phpt�� IU��ņ���������tests/fetch001.phpt�� IU��?2���������tests/info001.phpt�� IU��6���������tests/info002.phpt�� IU��\���������tests/lob001.phpt}�� IU}��DBz���������tests/lob002.phptA�� IUA��债ʶ���������tests/lob003.phptC�� IUC��#~���������tests/lob004.phpt�� IU��F)9���������tests/map001.phpt1 �� IU1 �����������tests/notify001.phptc�� IUc��n8v���������tests/persistent001.phptx�� IUx��k-���������tests/res001.phpt�� IU��*߮���������tests/reset001.phpt]�� IU]��]���������tests/savepoint001.phpt'�� IU'�� g���������tests/stm_bound001.phpt�� IU��:���������tests/stm_desc001.phptf�� IUf��L=���������tests/stm_desc002.phpto�� IUo��#���������tests/trans001.phpt�� IU��g ���������tests/trans002.phpt�� IU��s (���������tests/types001.phpt5�� IU5��}?���������tests/types002.phpto�� IUo��1rS���������tests/unbuffered001.phpt�� IU��I|���������tests/_setup.inc*��� IU*���U)���������tests/_skipif.inc'�� IU'��Eo������<?php namespace pharext\Cli; /** * Command line arguments */ class Args implements \ArrayAccess { /** * Optional option */ const OPTIONAL = 0x000; /** * Required Option */ const REQUIRED = 0x001; /** * Only one value, even when used multiple times */ const SINGLE = 0x000; /** * Aggregate an array, when used multiple times */ const MULTI = 0x010; /** * Option takes no argument */ const NOARG = 0x000; /** * Option requires an argument */ const REQARG = 0x100; /** * Option takes an optional argument */ const OPTARG = 0x200; /** * Option halts processing */ const HALT = 0x10000000; /** * Original option spec * @var array */ private $orig = []; /** * Compiled spec * @var array */ private $spec = []; /** * Parsed args * @var array */ private $args = []; /** * Compile the original spec * @param array|Traversable $spec */ public function __construct($spec = null) { if (is_array($spec) || $spec instanceof Traversable) { $this->compile($spec); } } /** * Compile the original spec * @param array|Traversable $spec * @return pharext\CliArgs self */ public function compile($spec) { foreach ($spec as $arg) { if (isset($arg[0])) { $this->spec["-".$arg[0]] = $arg; } $this->spec["--".$arg[1]] = $arg; $this->orig[] = $arg; } return $this; } /** * Get original spec * @return array */ public function getSpec() { return $this->orig; } /** * Get compiled spec * @return array */ public function getCompiledSpec() { return $this->spec; } /** * Parse command line arguments according to the compiled spec. * * The Generator yields any parsing errors. * Parsing will stop when all arguments are processed or the first option * flagged CliArgs::HALT was encountered. * * @param int $argc * @param array $argv * @return Generator */ public function parse($argc, array $argv) { for ($i = 0; $i < $argc; ++$i) { $o = $argv[$i]; if ($o{0} === '-' && strlen($o) > 2 && $o{1} !== '-') { // multiple short opts, .e.g -vps $argc += strlen($o) - 2; array_splice($argv, $i, 1, array_map(function($s) { return "-$s"; }, str_split(substr($o, 1)))); $o = $argv[$i]; } elseif ($o{0} === '-' && strlen($o) > 2 && $o{1} === '-' && 0 < ($eq = strpos($o, "="))) { $argc++; array_splice($argv, $i, 1, [ substr($o, 0, $eq++), substr($o, $eq) ]); $o = $argv[$i]; } if (!isset($this->spec[$o])) { yield sprintf("Unknown option %s", $o); } elseif (!$this->optAcceptsArg($o)) { $this[$o] = true; } elseif ($i+1 < $argc && !isset($this->spec[$argv[$i+1]])) { $this[$o] = $argv[++$i]; } elseif ($this->optRequiresArg($o)) { yield sprintf("Option --%s requires an argument", $this->optLongName($o)); } else { // OPTARG $this[$o] = $this->optDefaultArg($o); } if ($this->optHalts($o)) { return; } } } /** * Validate that all required options were given. * * The Generator yields any validation errors. * * @return Generator */ public function validate() { $required = array_filter($this->orig, function($spec) { return $spec[3] & self::REQUIRED; }); foreach ($required as $req) { if (!strlen($this[$req[0]])) { yield sprintf("Option --%s is required", $req[1]); } } } /** * Retreive the default argument of an option * @param string $o * @return mixed */ private function optDefaultArg($o) { $o = $this->opt($o); if (isset($this->spec[$o][4])) { return $this->spec[$o][4]; } return null; } /** * Retrieve the help message of an option * @param string $o * @return string */ private function optHelp($o) { $o = $this->opt($o); if (isset($this->spec[$o][2])) { return $this->spec[$o][2]; } return ""; } /** * Retrieve option's flags * @param string $o * @return int */ private function optFlags($o) { $o = $this->opt($o); if (isset($this->spec[$o])) { return $this->spec[$o][3]; } return null; } /** * Check whether an option is flagged for halting argument processing * @param string $o * @return boolean */ private function optHalts($o) { return $this->optFlags($o) & self::HALT; } /** * Check whether an option needs an argument * @param string $o * @return boolean */ private function optRequiresArg($o) { return $this->optFlags($o) & self::REQARG; } /** * Check wether an option accepts any argument * @param string $o * @return boolean */ private function optAcceptsArg($o) { return $this->optFlags($o) & 0xf00; } /** * Check whether an option can be used more than once * @param string $o * @return boolean */ private function optIsMulti($o) { return $this->optFlags($o) & self::MULTI; } /** * Retreive the long name of an option * @param string $o * @return string */ private function optLongName($o) { $o = $this->opt($o); return $this->spec[$o][1]; } /** * Retreive the short name of an option * @param string $o * @return string */ private function optShortName($o) { $o = $this->opt($o); return $this->spec[$o][0]; } /** * Retreive the canonical name (--long-name) of an option * @param string $o * @return string */ private function opt($o) { if ($o{0} !== '-') { if (strlen($o) > 1) { $o = "-$o"; } $o = "-$o"; } return $o; } /**@+ * Implements ArrayAccess and virtual properties */ function offsetExists($o) { $o = $this->opt($o); return isset($this->args[$o]); } function __isset($o) { return $this->offsetExists($o); } function offsetGet($o) { $o = $this->opt($o); if (isset($this->args[$o])) { return $this->args[$o]; } return $this->optDefaultArg($o); } function __get($o) { return $this->offsetGet($o); } function offsetSet($o, $v) { $osn = $this->optShortName($o); $oln = $this->optLongName($o); if ($this->optIsMulti($o)) { if (isset($osn)) { $this->args["-$osn"][] = $v; } $this->args["--$oln"][] = $v; } else { if (isset($osn)) { $this->args["-$osn"] = $v; } $this->args["--$oln"] = $v; } } function __set($o, $v) { $this->offsetSet($o, $v); } function offsetUnset($o) { unset($this->args["-".$this->optShortName($o)]); unset($this->args["--".$this->optLongName($o)]); } function __unset($o) { $this->offsetUnset($o); } /**@-*/ } <?php namespace pharext\Cli; use pharext\Cli\Args as CliArgs; use Phar; if (!function_exists("array_column")) { function array_column(array $array, $col, $idx = null) { $result = []; foreach ($array as $el) { if (isset($idx)) { $result[$el[$idx]] = $el[$col]; } else { $result[] = $el[$col]; } } return $result; } } trait Command { /** * Command line arguments * @var pharext\CliArgs */ private $args; /** * @inheritdoc * @see \pharext\Command::getArgs() */ public function getArgs() { return $this->args; } /** * Retrieve metadata of the currently running phar * @param string $key * @return mixed */ public function metadata($key = null) { $running = new Phar(Phar::running(false)); if ($key === "signature") { $sig = $running->getSignature(); return sprintf("%s signature of %s\n%s", $sig["hash_type"], $this->metadata("name"), chunk_split($sig["hash"], 64, "\n")); } $metadata = $running->getMetadata(); if (isset($key)) { return $metadata[$key]; } return $metadata; } /** * Output pharext vX.Y.Z header */ public function header() { if (!headers_sent()) { /* only display header, if we didn't generate any output yet */ printf("%s\n\n", $this->metadata("header")); } } /** * @inheritdoc * @see \pharext\Command::debug() */ public function debug($fmt) { if ($this->args->verbose) { vprintf($fmt, array_slice(func_get_args(), 1)); } } /** * @inheritdoc * @see \pharext\Command::info() */ public function info($fmt) { if (!$this->args->quiet) { vprintf($fmt, array_slice(func_get_args(), 1)); } } /** * @inheritdoc * @see \pharext\Command::warn() */ public function warn($fmt) { if (!$this->args->quiet) { if (!isset($fmt)) { $fmt = "%s\n"; $arg = error_get_last()["message"]; } else { $arg = array_slice(func_get_args(), 1); } vfprintf(STDERR, "Warning: $fmt", $arg); } } /** * @inheritdoc * @see \pharext\Command::error() */ public function error($fmt) { if (!isset($fmt)) { $fmt = "%s\n"; $arg = error_get_last()["message"]; } else { $arg = array_slice(func_get_args(), 1); } vfprintf(STDERR, "ERROR: $fmt", $arg); } /** * Output command line help message * @param string $prog */ public function help($prog) { printf("Usage:\n\n \$ %s", $prog); $flags = []; $required = []; $optional = []; foreach ($this->args->getSpec() as $spec) { if ($spec[3] & CliArgs::REQARG) { if ($spec[3] & CliArgs::REQUIRED) { $required[] = $spec; } else { $optional[] = $spec; } } else { $flags[] = $spec; } } if ($flags) { printf(" [-%s]", implode("", array_column($flags, 0))); } foreach ($required as $req) { printf(" -%s <arg>", $req[0]); } if ($optional) { printf(" [-%s <arg>]", implode("|-", array_column($optional, 0))); } printf("\n\n"); $spc = $this->args->getSpec(); $max = max(array_map("strlen", array_column($spc, 1))); $max += $max % 8 + 2; foreach ($spc as $spec) { if (isset($spec[0])) { printf(" -%s|", $spec[0]); } else { printf(" "); } printf("--%s ", $spec[1]); if ($spec[3] & CliArgs::REQARG) { printf("<arg> "); } elseif ($spec[3] & CliArgs::OPTARG) { printf("[<arg>]"); } else { printf(" "); } printf("%s%s", str_repeat(" ", $max-strlen($spec[1])+3*!isset($spec[0])), $spec[2]); if ($spec[3] & CliArgs::REQUIRED) { printf(" (REQUIRED)"); } if (isset($spec[4])) { printf(" [%s]", $spec[4]); } printf("\n"); } printf("\n"); } /** * Verbosity * @return boolean */ public function verbosity() { if ($this->args->verbose) { return true; } elseif ($this->args->quiet) { return false; } else { return null; } } } <?php namespace pharext; /** * Command interface */ interface Command { /** * Argument error */ const EARGS = 1; /** * Build error */ const EBUILD = 2; /** * Signature error */ const ESIGN = 3; /** * Extract/unpack error */ const EEXTRACT = 4; /** * Install error */ const EINSTALL = 5; /** * Retrieve command line arguments * @return pharext\CliArgs */ public function getArgs(); /** * Print debug message * @param string $fmt * @param string ...$args */ public function debug($fmt); /** * Print info * @param string $fmt * @param string ...$args */ public function info($fmt); /** * Print warning * @param string $fmt * @param string ...$args */ public function warn($fmt); /** * Print error * @param string $fmt * @param string ...$args */ public function error($fmt); /** * Execute the command * @param int $argc command line argument count * @param array $argv command line argument list */ public function run($argc, array $argv); } <?php namespace pharext; class Exception extends \Exception { public function __construct($message = null, $code = 0, $previous = null) { if (!isset($message)) { $last_error = error_get_last(); $message = $last_error["message"]; if (!$code) { $code = $last_error["type"]; } } parent::__construct($message, $code, $previous); } } <?php namespace pharext; /** * Execute system command */ class ExecCmd { /** * Sudo command, if the cmd needs escalated privileges * @var string */ private $sudo; /** * Executable of the cmd * @var string */ private $command; /** * Passthrough cmd output * @var bool */ private $verbose; /** * Output of cmd run * @var string */ private $output; /** * Return code of cmd run * @var int */ private $status; /** * @param string $command * @param bool verbose */ public function __construct($command, $verbose = false) { $this->command = $command; $this->verbose = $verbose; } /** * (Re-)set sudo command * @param string $sudo */ public function setSu($sudo = false) { $this->sudo = $sudo; } /** * Execute a program with escalated privileges handling interactive password prompt * @param string $command * @param bool $verbose * @return int exit status */ private function suExec($command, $verbose = null) { if (!($proc = proc_open($command, [STDIN,["pipe","w"],["pipe","w"]], $pipes))) { $this->status = -1; throw new Exception("Failed to run {$command}"); } $stdout = $pipes[1]; $passwd = 0; $checks = 10; while (!feof($stdout)) { $R = [$stdout]; $W = []; $E = []; if (!stream_select($R, $W, $E, null)) { continue; } $data = fread($stdout, 0x1000); /* only check a few times */ if ($passwd < $checks) { $passwd++; if (stristr($data, "password")) { $passwd = $checks + 1; printf("\n%s", $data); continue; } } elseif ($passwd > $checks) { /* new line after pw entry */ printf("\n"); $passwd = $checks; } if ($verbose === null) { print $this->progress($data, 0); } else { if ($verbose) { printf("%s", $data); } $this->output .= $data; } } if ($verbose === null) { $this->progress("", PHP_OUTPUT_HANDLER_FINAL); } return $this->status = proc_close($proc); } /** * Output handler that displays some progress while soaking output * @param string $string * @param int $flags * @return string */ private function progress($string, $flags) { static $counter = 0; static $symbols = ["\\","|","/","-"]; $this->output .= $string; if (false !== strpos($string, "\n")) { ++$counter; } return $flags & PHP_OUTPUT_HANDLER_FINAL ? " \r" : sprintf(" %s\r", $symbols[$counter % 4]); } /** * Run the command * @param array $args * @return \pharext\ExecCmd self * @throws \pharext\Exception */ public function run(array $args = null) { $exec = escapeshellcmd($this->command); if ($args) { $exec .= " ". implode(" ", array_map("escapeshellarg", (array) $args)); } if ($this->sudo) { $this->suExec(sprintf($this->sudo." 2>&1", $exec), $this->verbose); } elseif ($this->verbose) { ob_start(function($s) { $this->output .= $s; return $s; }, 1); passthru($exec, $this->status); ob_end_flush(); } elseif ($this->verbose !== false /* !quiet */) { ob_start([$this, "progress"], 1); passthru($exec . " 2>&1", $this->status); ob_end_flush(); } else { exec($exec ." 2>&1", $output, $this->status); $this->output = implode("\n", $output); } if ($this->status) { throw new Exception("Command {$exec} failed ({$this->status})"); } return $this; } /** * Retrieve exit code of cmd run * @return int */ public function getStatus() { return $this->status; } /** * Retrieve output of cmd run * @return string */ public function getOutput() { return $this->output; } } <?php namespace pharext; use pharext\Cli\Args as CliArgs; use pharext\Cli\Command as CliCommand; use Phar; use SplObjectStorage; /** * The extension install command executed by the extension phar */ class Installer implements Command { use CliCommand; /** * Cleanups * @var array */ private $cleanup = []; /** * Create the command */ public function __construct() { $this->args = new CliArgs([ ["h", "help", "Display help", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ["v", "verbose", "More output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["q", "quiet", "Less output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["p", "prefix", "PHP installation prefix if phpize is not in \$PATH, e.g. /opt/php7", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG], ["n", "common-name", "PHP common program name, e.g. php5 or zts-php", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG, "php"], ["c", "configure", "Additional extension configure flags, e.g. -c --with-flag", CliArgs::OPTIONAL|CliArgs::MULTI|CliArgs::REQARG], ["s", "sudo", "Installation might need increased privileges", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::OPTARG, "sudo -S %s"], ["i", "ini", "Activate in this php.ini instead of loaded default php.ini", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG], [null, "signature", "Show package signature", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "license", "Show package license", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "name", "Show package name", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "date", "Show package release date", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "release", "Show package release version", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "version", "Show pharext version", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ]); } /** * Perform cleaniup */ function __destruct() { foreach ($this->cleanup as $cleanup) { $cleanup->run(); } } private function extract(Phar $phar) { $temp = (new Task\Extract($phar))->run($this->verbosity()); $this->cleanup[] = new Task\Cleanup($temp); return $temp; } private function hooks(SplObjectStorage $phars) { $hook = []; foreach ($phars as $phar) { if (isset($phar["pharext_package.php"])) { $sdir = include $phar["pharext_package.php"]; if ($sdir instanceof SourceDir) { $this->args->compile($sdir->getArgs()); $hook[] = $sdir; } } } return $hook; } private function load() { $list = new SplObjectStorage(); $phar = new Phar(Phar::running(false)); $temp = $this->extract($phar); foreach ($phar as $entry) { $dep_file = $entry->getBaseName(); if (fnmatch("*.ext.phar*", $dep_file)) { $dep_phar = new Phar("$temp/$dep_file"); $list[$dep_phar] = $this->extract($dep_phar); } } /* the actual ext.phar at last */ $list[$phar] = $temp; return $list; } /** * @inheritdoc * @see \pharext\Command::run() */ public function run($argc, array $argv) { try { /* load the phar(s) */ $list = $this->load(); /* installer hooks */ $hook = $this->hooks($list); } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EEXTRACT); } /* standard arg stuff */ $errs = []; $prog = array_shift($argv); foreach ($this->args->parse(--$argc, $argv) as $error) { $errs[] = $error; } if ($this->args["help"]) { $this->header(); $this->help($prog); exit; } try { foreach (["signature", "name", "date", "license", "release", "version"] as $opt) { if ($this->args[$opt]) { printf("%s\n", $this->metadata($opt)); exit; } } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EARGS); } foreach ($this->args->validate() as $error) { $errs[] = $error; } if ($errs) { if (!$this->args["quiet"]) { $this->header(); } foreach ($errs as $err) { $this->error("%s\n", $err); } if (!$this->args["quiet"]) { $this->help($prog); } exit(self::EARGS); } try { /* post process hooks */ foreach ($hook as $sdir) { $sdir->setArgs($this->args); } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EARGS); } /* install packages */ try { foreach ($list as $phar) { $this->info("Installing %s ...\n", basename($phar->getPath())); $this->install($list[$phar]); $this->activate($list[$phar]); $this->info("Successfully installed %s!\n", basename($phar->getPath())); } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EINSTALL); } } /** * Phpize + trinity */ private function install($temp) { // phpize $phpize = new Task\Phpize($temp, $this->args->prefix, $this->args->{"common-name"}); $phpize->run($this->verbosity()); // configure $configure = new Task\Configure($temp, $this->args->configure, $this->args->prefix, $this->args{"common-name"}); $configure->run($this->verbosity()); // make $make = new Task\Make($temp); $make->run($this->verbosity()); // install $sudo = isset($this->args->sudo) ? $this->args->sudo : null; $install = new Task\Make($temp, ["install"], $sudo); $install->run($this->verbosity()); } private function activate($temp) { if ($this->args->ini) { $files = [realpath($this->args->ini)]; } else { $files = array_filter(array_map("trim", explode(",", php_ini_scanned_files()))); $files[] = php_ini_loaded_file(); } $sudo = isset($this->args->sudo) ? $this->args->sudo : null; $type = $this->metadata("type") ?: "extension"; $activate = new Task\Activate($temp, $files, $type, $this->args->prefix, $this->args{"common-name"}, $sudo); if (!$activate->run($this->verbosity())) { $this->info("Extension already activated ...\n"); } } } <?php namespace pharext\Openssl; use pharext\Exception; class PrivateKey { /** * Private key * @var string */ private $key; /** * Public key * @var string */ private $pub; /** * Read a private key * @param string $file * @param string $password * @throws \pharext\Exception */ function __construct($file, $password) { /* there appears to be a bug with refcount handling of this * resource; when the resource is stored as property, it cannot be * "coerced to a private key" on openssl_sign() later in another method */ $key = openssl_pkey_get_private("file://$file", $password); if (!is_resource($key)) { throw new Exception("Could not load private key"); } openssl_pkey_export($key, $this->key); $this->pub = openssl_pkey_get_details($key)["key"]; } /** * Sign the PHAR * @param \Phar $package */ function sign(\Phar $package) { $package->setSignatureAlgorithm(\Phar::OPENSSL, $this->key); } /** * Export the public key to a file * @param string $file * @throws \pharext\Exception */ function exportPublicKey($file) { if (!file_put_contents("$file.tmp", $this->pub) || !rename("$file.tmp", $file)) { throw new Exception; } } } <?php namespace pharext; use Phar; use pharext\Cli\Args as CliArgs; use pharext\Cli\Command as CliCommand; use pharext\Exception; /** * The extension packaging command executed by bin/pharext */ class Packager implements Command { use CliCommand; /** * Extension source directory * @var pharext\SourceDir */ private $source; /** * Cleanups * @var array */ private $cleanup = []; /** * Create the command */ public function __construct() { $this->args = new CliArgs([ ["h", "help", "Display this help", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ["v", "verbose", "More output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["q", "quiet", "Less output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["n", "name", "Extension name", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["r", "release", "Extension release version", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["s", "source", "Extension source directory", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["g", "git", "Use `git ls-tree` to determine file list", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["p", "pecl", "Use PECL package.xml to determine file list, name and release", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["d", "dest", "Destination directory", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG, "."], ["z", "gzip", "Create additional PHAR compressed with gzip", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["Z", "bzip", "Create additional PHAR compressed with bzip", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["S", "sign", "Sign the PHAR with a private key", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG], ["E", "zend", "Mark as Zend Extension", CliArgs::OPTIONAL|CliARgs::SINGLE|CliArgs::NOARG], [null, "signature", "Show pharext signature", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "license", "Show pharext license", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "version", "Show pharext version", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ]); } /** * Perform cleaniup */ function __destruct() { foreach ($this->cleanup as $cleanup) { $cleanup->run(); } } /** * @inheritdoc * @see \pharext\Command::run() */ public function run($argc, array $argv) { $errs = []; $prog = array_shift($argv); foreach ($this->args->parse(--$argc, $argv) as $error) { $errs[] = $error; } if ($this->args["help"]) { $this->header(); $this->help($prog); exit; } try { foreach (["signature", "license", "version"] as $opt) { if ($this->args[$opt]) { printf("%s\n", $this->metadata($opt)); exit; } } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EARGS); } try { /* source needs to be evaluated before CliArgs validation, * so e.g. name and version can be overriden and CliArgs * does not complain about missing arguments */ $this->loadSource(); } catch (\Exception $e) { $errs[] = $e->getMessage(); } foreach ($this->args->validate() as $error) { $errs[] = $error; } if ($errs) { if (!$this->args["quiet"]) { $this->header(); } foreach ($errs as $err) { $this->error("%s\n", $err); } printf("\n"); if (!$this->args["quiet"]) { $this->help($prog); } exit(self::EARGS); } $this->createPackage(); } /** * Download remote source * @param string $source * @return string local source */ private function download($source) { if ($this->args->git) { $task = new Task\GitClone($source); } else { /* print newline only once */ $done = false; $task = new Task\StreamFetch($source, function($bytes_pct) use(&$done) { if (!$done) { $this->info(" %3d%% [%s>%s] \r", floor($bytes_pct*100), str_repeat("=", round(50*$bytes_pct)), str_repeat(" ", round(50*(1-$bytes_pct))) ); if ($bytes_pct == 1) { $done = true; printf("\n"); } } }); } $local = $task->run($this->verbosity()); $this->cleanup[] = new Task\Cleanup($local); return $local; } /** * Extract local archive * @param stirng $source * @return string extracted directory */ private function extract($source) { try { $task = new Task\Extract($source); $dest = $task->run($this->verbosity()); } catch (\Exception $e) { if (false === strpos($e->getMessage(), "checksum mismatch")) { throw $e; } $dest = (new Task\PaxFixup($source))->run($this->verbosity()); } $this->cleanup[] = new Task\Cleanup($dest); return $dest; } /** * Localize a possibly remote source * @param string $source * @return string local source directory */ private function localize($source) { if (!stream_is_local($source)) { $source = $this->download($source); $this->cleanup[] = new Task\Cleanup($source); } $source = realpath($source); if (!is_dir($source)) { $source = $this->extract($source); $this->cleanup[] = new Task\Cleanup($source); if (!$this->args->git) { $source = (new Task\PeclFixup($source))->run($this->verbosity()); } } return $source; } /** * Load the source dir * @throws \pharext\Exception */ private function loadSource(){ if ($this->args["source"]) { $source = $this->localize($this->args["source"]); if ($this->args["pecl"]) { $this->source = new SourceDir\Pecl($source); } elseif ($this->args["git"]) { $this->source = new SourceDir\Git($source); } elseif (is_file("$source/pharext_package.php")) { $this->source = include "$source/pharext_package.php"; } else { $this->source = new SourceDir\Basic($source); } if (!$this->source instanceof SourceDir) { throw new Exception("Unknown source dir $source"); } foreach ($this->source->getPackageInfo() as $key => $val) { $this->args->$key = $val; } } } /** * Creates the extension phar */ private function createPackage() { try { $meta = array_merge($this->metadata(), [ "date" => date("Y-m-d"), "name" => $this->args->name, "release" => $this->args->release, "license" => @file_get_contents(current(glob($this->source->getBaseDir()."/LICENSE*"))), "stub" => "pharext_installer.php", "type" => $this->args->zend ? "zend_extension" : "extension", ]); $file = (new Task\PharBuild($this->source, $meta))->run($this->verbosity()); } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EBUILD); } try { if ($this->args->sign) { $this->info("Using private key to sign phar ...\n"); $pass = (new Task\Askpass)->run($this->verbosity()); $sign = new Task\PharSign($file, $this->args->sign, $pass); $pkey = $sign->run($this->verbosity()); } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::ESIGN); } if ($this->args->gzip) { try { $gzip = (new Task\PharCompress($file, Phar::GZ))->run(); $move = new Task\PharRename($gzip, $this->args->dest, $this->args->name ."-". $this->args->release); $name = $move->run($this->verbosity()); $this->info("Created gzipped phar %s\n", $name); if ($this->args->sign) { $sign = new Task\PharSign($name, $this->args->sign, $pass); $sign->run($this->verbosity())->exportPublicKey($name.".pubkey"); } } catch (\Exception $e) { $this->warn("%s\n", $e->getMessage()); } } if ($this->args->bzip) { try { $bzip = (new Task\PharCompress($file, Phar::BZ2))->run(); $move = new Task\PharRename($bzip, $this->args->dest, $this->args->name ."-". $this->args->release); $name = $move->run($this->verbosity()); $this->info("Created bzipped phar %s\n", $name); if ($this->args->sign) { $sign = new Task\PharSign($name, $this->args->sign, $pass); $sign->run($this->verbosity())->exportPublicKey($name.".pubkey"); } } catch (\Exception $e) { $this->warn("%s\n", $e->getMessage()); } } try { $move = new Task\PharRename($file, $this->args->dest, $this->args->name ."-". $this->args->release); $name = $move->run($this->verbosity()); $this->info("Created executable phar %s\n", $name); if (isset($pkey)) { $pkey->exportPublicKey($name.".pubkey"); } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EBUILD); } } } <?php namespace pharext\SourceDir; use pharext\Cli\Args; use pharext\SourceDir; use FilesystemIterator; use IteratorAggregate; use RecursiveCallbackFilterIterator; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; class Basic implements IteratorAggregate, SourceDir { private $path; public function __construct($path) { $this->path = $path; } public function getBaseDir() { return $this->path; } public function getPackageInfo() { return []; } public function getArgs() { return []; } public function setArgs(Args $args) { } public function filter($current, $key, $iterator) { $sub = $current->getSubPath(); if ($sub === ".git" || $sub === ".hg" || $sub === ".svn") { return false; } return true; } public function getIterator() { $rdi = new RecursiveDirectoryIterator($this->path, FilesystemIterator::CURRENT_AS_SELF | // needed for 5.5 FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::SKIP_DOTS); $rci = new RecursiveCallbackFilterIterator($rdi, [$this, "filter"]); $rii = new RecursiveIteratorIterator($rci); foreach ($rii as $path => $child) { if (!$child->isDir()) { yield $path; } } } } <?php namespace pharext\SourceDir; use pharext\Command; use pharext\Cli\Args; use pharext\SourceDir; /** * Extension source directory which is a git repo */ class Git implements \IteratorAggregate, SourceDir { /** * Base directory * @var string */ private $path; /** * @inheritdoc * @see \pharext\SourceDir::__construct() */ public function __construct($path) { $this->path = $path; } /** * @inheritdoc * @see \pharext\SourceDir::getBaseDir() */ public function getBaseDir() { return $this->path; } /** * @inheritdoc * @return array */ public function getPackageInfo() { return []; } /** * @inheritdoc * @return array */ public function getArgs() { return []; } /** * @inheritdoc */ public function setArgs(Args $args) { } /** * Generate a list of files by `git ls-files` * @return Generator */ private function generateFiles() { $pwd = getcwd(); chdir($this->path); if (($pipe = popen("git ls-tree -r --name-only HEAD", "r"))) { $path = realpath($this->path); while (!feof($pipe)) { if (strlen($file = trim(fgets($pipe)))) { /* there may be symlinks, so no realpath here */ yield "$path/$file"; } } pclose($pipe); } chdir($pwd); } /** * Implements IteratorAggregate * @see IteratorAggregate::getIterator() */ public function getIterator() { return $this->generateFiles(); } } <?php namespace pharext\SourceDir; use pharext\Cli\Args; use pharext\Exception; use pharext\SourceDir; use pharext\Tempfile; /** * A PECL extension source directory containing a v2 package.xml */ class Pecl implements \IteratorAggregate, SourceDir { /** * The package.xml * @var SimpleXmlElement */ private $sxe; /** * The base directory * @var string */ private $path; /** * The package.xml * @var string */ private $file; /** * @inheritdoc * @see \pharext\SourceDir::__construct() */ public function __construct($path) { if (is_file("$path/package2.xml")) { $sxe = simplexml_load_file($this->file = "$path/package2.xml"); } elseif (is_file("$path/package.xml")) { $sxe = simplexml_load_file($this->file = "$path/package.xml"); } else { throw new Exception("Missing package.xml in $path"); } $sxe->registerXPathNamespace("pecl", $sxe->getDocNamespaces()[""]); $this->sxe = $sxe; $this->path = realpath($path); } /** * @inheritdoc * @see \pharext\SourceDir::getBaseDir() */ public function getBaseDir() { return $this->path; } /** * Retrieve gathered package info * @return Generator */ public function getPackageInfo() { if (($name = $this->sxe->xpath("/pecl:package/pecl:name"))) { yield "name" => (string) $name[0]; } if (($release = $this->sxe->xpath("/pecl:package/pecl:version/pecl:release"))) { yield "release" => (string) $release[0]; } if ($this->sxe->xpath("/pecl:package/pecl:zendextsrcrelease")) { yield "zend" => true; } } /** * @inheritdoc * @see \pharext\SourceDir::getArgs() */ public function getArgs() { $configure = $this->sxe->xpath("/pecl:package/pecl:extsrcrelease/pecl:configureoption"); foreach ($configure as $cfg) { yield [null, $cfg["name"], ucfirst($cfg["prompt"]), Args::OPTARG, strlen($cfg["default"]) ? $cfg["default"] : null]; } $configure = $this->sxe->xpath("/pecl:package/pecl:zendextsrcrelease/pecl:configureoption"); foreach ($configure as $cfg) { yield [null, $cfg["name"], ucfirst($cfg["prompt"]), Args::OPTARG, strlen($cfg["default"]) ? $cfg["default"] : null]; } } /** * @inheritdoc * @see \pharext\SourceDir::setArgs() */ public function setArgs(Args $args) { $configure = $this->sxe->xpath("/pecl:package/pecl:extsrcrelease/pecl:configureoption"); foreach ($configure as $cfg) { if (isset($args[$cfg["name"]])) { $args->configure = "--{$cfg["name"]}={$args[$cfg["name"]]}"; } } $configure = $this->sxe->xpath("/pecl:package/pecl:zendextsrcrelease/pecl:configureoption"); foreach ($configure as $cfg) { if (isset($args[$cfg["name"]])) { $args->configure = "--{$cfg["name"]}={$args[$cfg["name"]]}"; } } } /** * Compute the path of a file by parent dir nodes * @param \SimpleXMLElement $ele * @return string */ private function dirOf($ele) { $path = ""; while (($ele = current($ele->xpath(".."))) && $ele->getName() == "dir") { $path = trim($ele["name"], "/") ."/". $path ; } return trim($path, "/"); } /** * Generate a list of files from the package.xml * @return Generator */ private function generateFiles() { /* hook */ $temp = tmpfile(); fprintf($temp, "<?php\nreturn new %s(__DIR__);\n", get_class($this)); rewind($temp); yield "pharext_package.php" => $temp; /* deps */ $dependencies = $this->sxe->xpath("/pecl:package/pecl:dependencies/pecl:required/pecl:package"); foreach ($dependencies as $key => $dep) { if (($glob = glob("{$this->path}/{$dep->name}-*.ext.phar*"))) { usort($glob, function($a, $b) { return version_compare( substr($a, strpos(".ext.phar", $a)), substr($b, strpos(".ext.phar", $b)) ); }); yield end($glob); } } /* files */ yield realpath($this->file); foreach ($this->sxe->xpath("//pecl:file") as $file) { yield realpath($this->path ."/". $this->dirOf($file) ."/". $file["name"]); } } /** * Implements IteratorAggregate * @see IteratorAggregate::getIterator() */ public function getIterator() { return $this->generateFiles(); } } <?php namespace pharext; /** * Source directory interface, which should yield file names to package on traversal */ interface SourceDir extends \Traversable { /** * Retrieve the base directory * @return string */ public function getBaseDir(); /** * Retrieve gathered package info * @return array|Traversable */ public function getPackageInfo(); /** * Provide installer command line args * @return array|Traversable */ public function getArgs(); /** * Process installer command line args * @param \pharext\Cli\Args $args */ public function setArgs(Cli\Args $args); } <?php namespace pharext\Task; use pharext\Exception; use pharext\ExecCmd; use pharext\Task; use pharext\Tempfile; /** * PHP INI activation */ class Activate implements Task { /** * @var string */ private $cwd; /** * @var array */ private $inis; /** * @var string */ private $type; /** * @var string */ private $php_config; /** * @var string */ private $sudo; /** * @param string $cwd working directory * @param array $inis custom INI or list of loaded/scanned INI files * @param string $type extension or zend_extension * @param string $prefix install prefix, e.g. /usr/local * @param string $common_name PHP programs common name, e.g. php5 * @param string $sudo sudo command * @throws \pharext\Exception */ public function __construct($cwd, array $inis, $type = "extension", $prefix = null, $common_name = "php", $sudo = null) { $this->cwd = $cwd; $this->type = $type; $this->sudo = $sudo; if (!$this->inis = $inis) { throw new Exception("No PHP INIs given"); } $cmd = $common_name . "-config"; if (isset($prefix)) { $cmd = $prefix . "/bin/" . $cmd; } $this->php_config = $cmd; } /** * @param bool $verbose * @return boolean false, if extension was already activated */ public function run($verbose = false) { if ($verbose !== false) { printf("Running INI activation ...\n"); } $extension = basename(current(glob("{$this->cwd}/modules/*.so"))); if ($this->type === "zend_extension") { $pattern = preg_quote((new ExecCmd($this->php_config))->run(["--extension-dir"])->getOutput() . "/$extension", "/"); } else { $pattern = preg_quote($extension, "/"); } foreach ($this->inis as $file) { if ($verbose) { printf("Checking %s ...\n", $file); } $temp = new Tempfile("phpini"); foreach (file($file) as $line) { if (preg_match("/^\s*{$this->type}\s*=\s*[\"']?{$pattern}[\"']?\s*(;.*)?\$/", $line)) { return false; } fwrite($temp->getStream(), $line); } } /* not found; append to last processed file, which is the main by default */ if ($verbose) { printf("Activating in %s ...\n", $file); } fprintf($temp->getStream(), $this->type . "=%s\n", $extension); $temp->closeStream(); $path = $temp->getPathname(); $stat = stat($file); // owner transfer $ugid = sprintf("%d:%d", $stat["uid"], $stat["gid"]); $cmd = new ExecCmd("chown", $verbose); if (isset($this->sudo)) { $cmd->setSu($this->sudo); } $cmd->run([$ugid, $path]); // permission transfer $perm = decoct($stat["mode"] & 0777); $cmd = new ExecCmd("chmod", $verbose); if (isset($this->sudo)) { $cmd->setSu($this->sudo); } $cmd->run([$perm, $path]); // rename $cmd = new ExecCmd("mv", $verbose); if (isset($this->sudo)) { $cmd->setSu($this->sudo); } $cmd->run([$path, $file]); if ($verbose) { printf("Replaced %s ...\n", $file); } return true; } } <?php namespace pharext\Task; use pharext\Task; /** * Ask password on console */ class Askpass implements Task { /** * @var string */ private $prompt; /** * @param string $prompt */ public function __construct($prompt = "Password:") { $this->prompt = $prompt; } /** * @param bool $verbose * @return string */ public function run($verbose = false) { system("stty -echo"); printf("%s ", $this->prompt); $pass = fgets(STDIN, 1024); printf("\n"); system("stty echo"); if (substr($pass, -1) == "\n") { $pass = substr($pass, 0, -1); } return $pass; } } <?php namespace pharext\Task; use pharext\Task; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; /** * List all library files of pharext to bundle with a phar */ class BundleGenerator implements Task { /** * @param bool $verbose * @return Generator */ public function run($verbose = false) { if ($verbose) { printf("Packaging pharext ... \n"); } $rdi = new RecursiveDirectoryIterator(dirname(dirname(__DIR__))); $rii = new RecursiveIteratorIterator($rdi); for ($rii->rewind(); $rii->valid(); $rii->next()) { if (!$rii->isDot()) { yield $rii->getSubPathname() => $rii->key(); } } } } <?php namespace pharext\Task; use pharext\Task; use FilesystemIterator; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; /** * Recursively cleanup FS entries */ class Cleanup implements Task { /** * @var string */ private $rm; public function __construct($rm) { $this->rm = $rm; } /** * @param bool $verbose */ public function run($verbose = false) { if ($verbose) { printf("Cleaning up %s ...\n", $this->rm); } if ($this->rm instanceof Tempfile) { unset($this->rm); } elseif (is_dir($this->rm)) { $rdi = new RecursiveDirectoryIterator($this->rm, FilesystemIterator::CURRENT_AS_SELF | // needed for 5.5 FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::SKIP_DOTS); $rii = new RecursiveIteratorIterator($rdi, RecursiveIteratorIterator::CHILD_FIRST); foreach ($rii as $path => $child) { if ($child->isDir()) { rmdir($path); } else { unlink($path); } } rmdir($this->rm); } else { @unlink($this->rm); } } } <?php namespace pharext\Task; use pharext\Exception; use pharext\ExecCmd; use pharext\Task; /** * Runs extension's configure */ class Configure implements Task { /** * @var array */ private $args; /** * @var string */ private $cwd; /** * @param string $cwd working directory * @param array $args configure args * @param string $prefix install prefix, e.g. /usr/local * @param string $common_name PHP programs common name, e.g. php5 */ public function __construct($cwd, array $args = null, $prefix = null, $common_name = "php") { $this->cwd = $cwd; $cmd = $common_name . "-config"; if (isset($prefix)) { $cmd = $prefix . "/bin/" . $cmd; } $this->args = ["--with-php-config=$cmd"]; if ($args) { $this->args = array_merge($this->args, $args); } } public function run($verbose = false) { if ($verbose !== false) { printf("Running ./configure ...\n"); } $pwd = getcwd(); if (!chdir($this->cwd)) { throw new Exception; } try { $cmd = new ExecCmd("./configure", $verbose); $cmd->run($this->args); } finally { chdir($pwd); } } } <?php namespace pharext\Task; use pharext\Task; use pharext\Tempdir; use Phar; use PharData; /** * Extract a package archive */ class Extract implements Task { /** * @var Phar(Data) */ private $source; /** * @param mixed $source archive location */ public function __construct($source) { if ($source instanceof Phar || $source instanceof PharData) { $this->source = $source; } else { $this->source = new PharData($source); } } /** * @param bool $verbose * @return \pharext\Tempdir */ public function run($verbose = false) { if ($verbose) { printf("Extracting %s ...\n", basename($this->source->getPath())); } $dest = new Tempdir("extract"); $this->source->extractTo($dest); return $dest; } } <?php namespace pharext\Task; use pharext\ExecCmd; use pharext\Task; use pharext\Tempdir; /** * Clone a git repo */ class GitClone implements Task { /** * @var string */ private $source; /** * @param string $source git repo location */ public function __construct($source) { $this->source = $source; } /** * @param bool $verbose * @return \pharext\Tempdir */ public function run($verbose = false) { if ($verbose !== false) { printf("Fetching %s ...\n", $this->source); } $local = new Tempdir("gitclone"); $cmd = new ExecCmd("git", $verbose); $cmd->run(["clone", $this->source, $local]); return $local; } } <?php namespace pharext\Task; use pharext\ExecCmd; use pharext\Exception; use pharext\Task; /** * Run make in the source dir */ class Make implements Task { /** * @var string */ private $cwd; /** * @var array */ private $args; /** * @var string */ private $sudo; /** * * @param string $cwd working directory * @param array $args make's arguments * @param string $sudo sudo command */ public function __construct($cwd, array $args = null, $sudo = null) { $this->cwd = $cwd; $this->sudo = $sudo; $this->args = $args; } /** * * @param bool $verbose * @throws \pharext\Exception */ public function run($verbose = false) { if ($verbose !== false) { printf("Running make"); if ($this->args) { foreach ($this->args as $arg) { printf(" %s", $arg); } } printf(" ...\n"); } $pwd = getcwd(); if (!chdir($this->cwd)) { throw new Exception; } try { $cmd = new ExecCmd("make", $verbose); if (isset($this->sudo)) { $cmd->setSu($this->sudo); } $args = $this->args; if (!$verbose) { $args = array_merge((array) $args, ["-s"]); } $cmd->run($args); } finally { chdir($pwd); } } } <?php namespace pharext\Task; use pharext\Exception; use pharext\Task; use pharext\Tempfile; class PaxFixup implements Task { private $source; public function __construct($source) { $this->source = $source; } private function openArchive($source) { $hdr = file_get_contents($source, false, null, 0, 3); if ($hdr === "\x1f\x8b\x08") { $fd = fopen("compress.zlib://$source", "r"); } elseif ($hdr === "BZh") { $fd = fopen("compress.bzip2://$source", "r"); } else { $fd = fopen($source, "r"); } if (!is_resource($fd)) { throw new Exception; } return $fd; } public function run($verbose = false) { if ($verbose !== false) { printf("Fixing up a tarball with global pax header ...\n"); } $temp = new Tempfile("paxfix"); stream_copy_to_stream($this->openArchive($this->source), $temp->getStream(), -1, 1024); $temp->closeStream(); return (new Extract((string) $temp))->run($verbose); } }<?php namespace pharext\Task; use pharext\Exception; use pharext\Task; /** * Fixup package.xml files in an extracted PECL dir */ class PeclFixup implements Task { /** * @var string */ private $source; /** * @param string $source source directory */ public function __construct($source) { $this->source = $source; } /** * @param bool $verbose * @return string sanitized source location * @throws \pahrext\Exception */ public function run($verbose = false) { if ($verbose !== false) { printf("Sanitizing PECL dir ...\n"); } $dirs = glob("{$this->source}/*", GLOB_ONLYDIR); $files = array_diff(glob("{$this->source}/*"), $dirs); $check = array_reduce($files, function($r, $v) { return $v && fnmatch("package*.xml", basename($v)); }, true); if (count($dirs) !== 1 || !$check) { throw new Exception("Does not look like an extracted PECL dir: {$this->source}"); } $dest = current($dirs); foreach ($files as $file) { if ($verbose) { printf("Moving %s into %s ...\n", basename($file), basename($dest)); } if (!rename($file, "$dest/" . basename($file))) { throw new Exception; } } return $dest; } } <?php namespace pharext\Task; use pharext\Exception; use pharext\SourceDir; use pharext\Task; use pharext\Tempname; use Phar; /** * Build phar */ class PharBuild implements Task { /** * @var \pharext\SourceDir */ private $source; /** * @var array */ private $meta; /** * @var bool */ private $readonly; /** * @param SourceDir $source extension source directory * @param array $meta phar meta data * @param bool $readonly whether the stub has -dphar.readonly=1 set */ public function __construct(SourceDir $source = null, array $meta = null, $readonly = true) { $this->source = $source; $this->meta = $meta; $this->readonly = $readonly; } /** * @param bool $verbose * @return \pharext\Tempname * @throws \pharext\Exception */ public function run($verbose = false) { /* Phar::compress() and ::convert*() use strtok("."), ugh! * so, be sure to not use any other dots in the filename * except for .phar */ $temp = new Tempname("", "-pharext.phar"); $phar = new Phar($temp); $phar->startBuffering(); if ($this->meta) { $phar->setMetadata($this->meta); if (isset($this->meta["stub"])) { $phar->setDefaultStub($this->meta["stub"]); $phar->setStub("#!/usr/bin/php -dphar.readonly=" . intval($this->readonly) ."\n". $phar->getStub()); } } $phar->buildFromIterator((new Task\BundleGenerator)->run()); if ($this->source) { if ($verbose) { $bdir = $this->source->getBaseDir(); $blen = strlen($bdir); foreach ($this->source as $index => $file) { if (is_resource($file)) { printf("Packaging %s ...\n", $index); $phar[$index] = $file; } else { printf("Packaging %s ...\n", $index = trim(substr($file, $blen), "/")); $phar->addFile($file, $index); } } } else { $phar->buildFromIterator($this->source, $this->source->getBaseDir()); } } $phar->stopBuffering(); if (!chmod($temp, fileperms($temp) | 0111)) { throw new Exception; } return $temp; } }<?php namespace pharext\Task; use pharext\Task; use Phar; /** * Clone a compressed copy of a phar */ class PharCompress implements Task { /** * @var string */ private $file; /** * @var Phar */ private $package; /** * @var int */ private $encoding; /** * @var string */ private $extension; /** * @param string $file path to the original phar * @param int $encoding Phar::GZ or Phar::BZ2 */ public function __construct($file, $encoding) { $this->file = $file; $this->package = new Phar($file); $this->encoding = $encoding; switch ($encoding) { case Phar::GZ: $this->extension = ".gz"; break; case Phar::BZ2: $this->extension = ".bz2"; break; } } /** * @param bool $verbose * @return string */ public function run($verbose = false) { if ($verbose) { printf("Compressing %s ...\n", basename($this->package->getPath())); } $phar = $this->package->compress($this->encoding); $meta = $phar->getMetadata(); if (isset($meta["stub"])) { /* drop shebang */ $phar->setDefaultStub($meta["stub"]); } return $this->file . $this->extension; } } <?php namespace pharext\Task; use pharext\Exception; use pharext\Task; /** * Rename the phar archive */ class PharRename implements Task { /** * @var string */ private $phar; /** * @var string */ private $dest; /** * @var string */ private $name; /** * @param string $phar path to phar * @param string $dest destination dir * @param string $name package name */ public function __construct($phar, $dest, $name) { $this->phar = $phar; $this->dest = $dest; $this->name = $name; } /** * @param bool $verbose * @return string path to renamed phar * @throws \pharext\Exception */ public function run($verbose = false) { $extension = substr(strstr($this->phar, "-pharext.phar"), 8); $name = sprintf("%s/%s.ext%s", $this->dest, $this->name, $extension); if ($verbose) { printf("Renaming %s to %s ...\n", basename($this->phar), basename($name)); } if (!rename($this->phar, $name)) { throw new Exception; } return $name; } } <?php namespace pharext\Task; use pharext\Openssl; use pharext\Task; use Phar; /** * Sign the phar with a private key */ class PharSign implements Task { /** * @var Phar */ private $phar; /** * @var \pharext\Openssl\PrivateKey */ private $pkey; /** * * @param mixed $phar phar instance or path to phar * @param string $pkey path to private key * @param string $pass password for the private key */ public function __construct($phar, $pkey, $pass) { if ($phar instanceof Phar || $phar instanceof PharData) { $this->phar = $phar; } else { $this->phar = new Phar($phar); } $this->pkey = new Openssl\PrivateKey($pkey, $pass); } /** * @param bool $verbose * @return \pharext\Openssl\PrivateKey */ public function run($verbose = false) { if ($verbose) { printf("Signing %s ...\n", basename($this->phar->getPath())); } $this->pkey->sign($this->phar); return $this->pkey; } } <?php namespace pharext\Task; use pharext\Exception; use pharext\ExecCmd; use pharext\Task; /** * Run phpize in the extension source directory */ class Phpize implements Task { /** * @var string */ private $phpize; /** * * @var string */ private $cwd; /** * @param string $cwd working directory * @param string $prefix install prefix, e.g. /usr/local * @param string $common_name PHP program common name, e.g. php5 */ public function __construct($cwd, $prefix = null, $common_name = "php") { $this->cwd = $cwd; $cmd = $common_name . "ize"; if (isset($prefix)) { $cmd = $prefix . "/bin/" . $cmd; } $this->phpize = $cmd; } /** * @param bool $verbose * @throws \pharext\Exception */ public function run($verbose = false) { if ($verbose !== false) { printf("Running %s ...\n", $this->phpize); } $pwd = getcwd(); if (!chdir($this->cwd)) { throw new Exception; } try { $cmd = new ExecCmd($this->phpize, $verbose); $cmd->run(); } finally { chdir($pwd); } } } <?php namespace pharext\Task; use pharext\Exception; use pharext\Task; use pharext\Tempfile; /** * Fetch a remote archive */ class StreamFetch implements Task { /** * @var string */ private $source; /** * @var callable */ private $progress; /** * @param string $source remote file location * @param callable $progress progress callback */ public function __construct($source, callable $progress) { $this->source = $source; $this->progress = $progress; } private function createStreamContext() { $progress = $this->progress; /* avoid bytes_max bug of older PHP versions */ $maxbytes = 0; return stream_context_create([],["notification" => function($notification, $severity, $message, $code, $bytes_cur, $bytes_max) use($progress, &$maxbytes) { if ($bytes_max > $maxbytes) { $maxbytes = $bytes_max; } switch ($notification) { case STREAM_NOTIFY_CONNECT: $progress(0); break; case STREAM_NOTIFY_PROGRESS: $progress($maxbytes > 0 ? $bytes_cur/$maxbytes : .5); break; case STREAM_NOTIFY_COMPLETED: /* this is sometimes not generated, why? */ $progress(1); break; } }]); } /** * @param bool $verbose * @return \pharext\Task\Tempfile * @throws \pharext\Exception */ public function run($verbose = false) { if ($verbose !== false) { printf("Fetching %s ...\n", $this->source); } $context = $this->createStreamContext(); if (!$remote = fopen($this->source, "r", false, $context)) { throw new Exception; } $local = new Tempfile("remote"); if (!stream_copy_to_stream($remote, $local->getStream())) { throw new Exception; } $local->closeStream(); /* STREAM_NOTIFY_COMPLETED is not generated, see above */ call_user_func($this->progress, 1); return $local; } } <?php namespace pharext; /** * Simple task interface */ interface Task { public function run($verbose = false); } <?php namespace pharext; /** * Create a temporary directory */ class Tempdir extends \SplFileInfo { /** * @param string $prefix prefix to uniqid() * @throws \pharext\Exception */ public function __construct($prefix) { $temp = new Tempname($prefix); if (!is_dir($temp) && !mkdir($temp, 0700, true)) { throw new Exception("Could not create tempdir: ".error_get_last()["message"]); } parent::__construct($temp); } } <?php namespace pharext; /** * Create a new temporary file */ class Tempfile extends \SplFileInfo { /** * @var resource */ private $handle; /** * @param string $prefix uniqid() prefix * @param string $suffix e.g. file extension * @throws \pharext\Exception */ public function __construct($prefix, $suffix = ".tmp") { $tries = 0; $omask = umask(077); do { $path = new Tempname($prefix, $suffix); $this->handle = fopen($path, "x"); } while (!is_resource($this->handle) && $tries++ < 10); umask($omask); if (!is_resource($this->handle)) { throw new Exception("Could not create temporary file"); } parent::__construct($path); } /** * Unlink the file */ public function __destruct() { @unlink($this->getPathname()); } /** * Close the stream */ public function closeStream() { fclose($this->handle); } /** * Retrieve the stream resource * @return resource */ public function getStream() { return $this->handle; } } <?php namespace pharext; use pharext\Exception; /** * A temporary file/directory name */ class Tempname { /** * @var string */ private $name; /** * @param string $prefix uniqid() prefix * @param string $suffix e.g. file extension */ public function __construct($prefix, $suffix = null) { $temp = sys_get_temp_dir() . "/pharext-" . posix_getlogin(); if (!is_dir($temp) && !mkdir($temp, 0700, true)) { throw new Exception; } $this->name = $temp ."/". uniqid($prefix) . $suffix; } /** * @return string */ public function __toString() { return (string) $this->name; } } <?php namespace pharext; const VERSION = "3.0.1"; <?php /** * The installer sub-stub for extension phars */ spl_autoload_register(function($c) { return include strtr($c, "\\_", "//") . ".php"; }); $installer = new pharext\Installer(); $installer->run($argc, $argv); <?php /** * The packager sub-stub for bin/pharext */ spl_autoload_register(function($c) { return include strtr($c, "\\_", "//") . ".php"; }); $packager = new pharext\Packager(); $packager->run($argc, $argv); <?php return new pharext\SourceDir\Pecl(__DIR__); <?xml version="1.0" encoding="UTF-8"?> <package packagerversion="1.4.11" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd"> <name>pq</name> <channel>pecl.php.net</channel> <summary>PostgreSQL client library (libpq) binding</summary> <description> Documents: http://devel-m6w6.rhcloud.com/mdref/pq Highlights: * Nearly complete support for asynchronous usage: http://devel-m6w6.rhcloud.com/mdref/pq/Connection/%3A%20Asynchronous%20Usage * Extended type support by pg_type: http:/devel-m6w6.rhcloud.com/mdref/pq/Types/%3A%20Overview * Fetching simple multi-dimensional array maps: http:/devel-m6w6.rhcloud.com/mdref/pq/Result/map * Working Gateway implementation: https://http://devel-m6w6.rhcloud.com/mdref/pq-gateway </description> <lead> <name>Michael Wallner</name> <user>mike</user> <email>mike@php.net</email> <active>yes</active> </lead> <developer> <name>Chris Wright</name> <user>daverandom</user> <email>daverandom@php.net</email> <active>yes</active> </developer> <date>2014-12-17</date> <version> <release>0.6.0dev</release> <api>0.6.0</api> </version> <stability> <release>beta</release> <api>beta</api> </stability> <license>BSD, revised</license> <notes><![CDATA[ * Fixed crash with result iterator when the iterator exists longer than the result * Added pq\Statement::deallocate{,Async}() and pq\Statement::prepare{,Async}() methods * Added pq\Statement::$query and pq\Statment::$types readonly properties ]]></notes> <contents> <dir name="/"> <file role="doc" name="CREDITS" /> <file role="doc" name="LICENSE" /> <file role="src" name="config.m4" /> <file role="src" name="config9.m4" /> <file role="src" name="php_pq.h" /> <file role="src" name="php_pq_type.awk" /> <file role="src" name="php_pq_type.h" /> <dir name="src"> <file role="src" name="php_pq_callback.c" /> <file role="src" name="php_pq_callback.h" /> <file role="src" name="php_pqcancel.c" /> <file role="src" name="php_pqcancel.h" /> <file role="src" name="php_pqconn.c" /> <file role="src" name="php_pqconn_event.c" /> <file role="src" name="php_pqconn_event.h" /> <file role="src" name="php_pqconn.h" /> <file role="src" name="php_pqcopy.c" /> <file role="src" name="php_pqcopy.h" /> <file role="src" name="php_pqcur.c" /> <file role="src" name="php_pqcur.h" /> <file role="src" name="php_pqexc.c" /> <file role="src" name="php_pqexc.h" /> <file role="src" name="php_pqlob.c" /> <file role="src" name="php_pqlob.h" /> <file role="src" name="php_pq_misc.c" /> <file role="src" name="php_pq_misc.h" /> <file role="src" name="php_pq_module.c" /> <file role="src" name="php_pq_object.c" /> <file role="src" name="php_pq_object.h" /> <file role="src" name="php_pq_params.c" /> <file role="src" name="php_pq_params.h" /> <file role="src" name="php_pqres.c" /> <file role="src" name="php_pqres.h" /> <file role="src" name="php_pqstm.c" /> <file role="src" name="php_pqstm.h" /> <file role="src" name="php_pqtxn.c" /> <file role="src" name="php_pqtxn.h" /> <file role="src" name="php_pqtypes.c" /> <file role="src" name="php_pqtypes.h" /> </dir> <dir name="tests"> <file role="test" name="async001.phpt" /> <file role="test" name="async002.phpt" /> <file role="test" name="async003.phpt" /> <file role="test" name="async004.phpt" /> <file role="test" name="async005.phpt" /> <file role="test" name="async006.phpt" /> <file role="test" name="async007.phpt" /> <file role="test" name="async008.phpt" /> <file role="test" name="basic001.phpt" /> <file role="test" name="basic002.phpt" /> <file role="test" name="bound002.phpt" /> <file role="test" name="cancel001.phpt" /> <file role="test" name="conv001.phpt" /> <file role="test" name="copy001.phpt" /> <file role="test" name="cursor001.phpt" /> <file role="test" name="encoding001.phpt" /> <file role="test" name="exceptions001.phpt" /> <file role="test" name="exceptions002.phpt" /> <file role="test" name="fetch001.phpt" /> <file role="test" name="info001.phpt" /> <file role="test" name="info002.phpt" /> <file role="test" name="lob001.phpt" /> <file role="test" name="lob002.phpt" /> <file role="test" name="lob003.phpt" /> <file role="test" name="lob004.phpt" /> <file role="test" name="map001.phpt" /> <file role="test" name="notify001.phpt" /> <file role="test" name="persistent001.phpt" /> <file role="test" name="res001.phpt" /> <file role="test" name="reset001.phpt" /> <file role="test" name="savepoint001.phpt" /> <file role="test" name="stm_bound001.phpt" /> <file role="test" name="stm_desc001.phpt" /> <file role="test" name="stm_desc002.phpt" /> <file role="test" name="trans001.phpt" /> <file role="test" name="trans002.phpt" /> <file role="test" name="types001.phpt" /> <file role="test" name="types002.phpt" /> <file role="test" name="unbuffered001.phpt" /> <file role="test" name="_setup.inc" /> <file role="test" name="_skipif.inc" /> </dir> </dir> </contents> <dependencies> <required> <php> <min>5.4</min> </php> <pearinstaller> <min>1.4.0</min> </pearinstaller> <package> <name>raphf</name> <channel>pecl.php.net</channel> <min>1.0.4</min> <providesextension>raphf</providesextension> </package> </required> </dependencies> <providesextension>pq</providesextension> <extsrcrelease> <configureoption default="yes" name="with-pq" prompt="where to find the postgresql library/headers" /> </extsrcrelease> </package> pq Michael Wallner Copyright (c) 2013, Michael Wallner <mike@php.net>. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. dnl phpize stub of config9.m4 for pecl/pq sinclude(config9.m4) PHP_ARG_WITH(pq, [whether to enable libpq (PostgreSQL) support], [ --with-pq[=DIR] Include libpq support]) if test "$PHP_PQ" != "no"; then SEARCH_PATH="/usr/local /usr /usr/include/postgresql /opt" if test "$PHP_PQ" != "yes"; then SEARCH_PATH="$PHP_PQ $SEARCH_PATH" fi for i in $SEARCH_PATH; do AC_MSG_CHECKING(for $i/libpq-events.h) if test -f "$i/libpq-events.h"; then PQ_DIR=$i AC_MSG_RESULT(yep) break fi AC_MSG_RESULT(nope) AC_MSG_CHECKING(for $i/include/libpq-events.h) if test -f "$i/include/libpq-events.h"; then PQ_DIR=$i/include AC_MSG_RESULT(yep) break fi AC_MSG_RESULT(nope) done if test -z "$PQ_DIR"; then AC_MSG_ERROR(could not find include/libpq-events.h) fi PHP_ADD_INCLUDE($PQ_DIR) ifdef([AC_PROG_EGREP], [ AC_PROG_EGREP ], [ AC_CHECK_PROG(EGREP, egrep, egrep) ]) dnl dnl PQ_CHECK_CONST(name) dnl AC_DEFUN([PQ_CHECK_CONST], [ AC_MSG_CHECKING(for $1) if $EGREP -q $1 $PQ_DIR/libpq-fe.h; then AC_DEFINE(HAVE_$1, 1, [Have $1]) AC_MSG_RESULT(yep) else AC_MSG_RESULT(nope) fi ]) PQ_CHECK_CONST(PGRES_SINGLE_TUPLE) PQ_CHECK_CONST(PGRES_COPY_BOTH) dnl dnl PQ_CHECK_FUNC(sym, fail-hard) dnl AC_DEFUN([PQ_CHECK_FUNC], [ FAIL_HARD=$2 PHP_CHECK_LIBRARY(pq, $1, [ AC_DEFINE([HAVE_]translit($1,a-z,A-Z), 1, Have $1) ], [ if test -n "$FAIL_HARD"; then if "$FAIL_HARD"; then AC_MSG_ERROR(could not find $PQ_SYM in -lpq) fi fi ], [ -L$PQ_DIR/$PHP_LIBDIR ]) ]) PQ_CHECK_FUNC(PQregisterEventProc, true) PHP_ADD_LIBRARY_WITH_PATH(pq, $PQ_DIR/$PHP_LIBDIR, PQ_SHARED_LIBADD) PHP_SUBST(PQ_SHARED_LIBADD) PQ_CHECK_FUNC(PQlibVersion) PQ_CHECK_FUNC(PQconninfo) PQ_CHECK_FUNC(PQsetSingleRowMode) dnl dnl PQ_HAVE_PHP_EXT(name[, code-if-yes[, code-if-not]]) dnl AC_DEFUN([PQ_HAVE_PHP_EXT], [ extname=$1 haveext=$[PHP_]translit($1,a-z_-,A-Z__) AC_MSG_CHECKING([for ext/$extname support]) if test "$haveext" != "no" && test "x$haveext" != "x"; then [PHP_PQ_HAVE_EXT_]translit($1,a-z_-,A-Z__)=1 AC_MSG_RESULT([yes]) $2 elif test -x "$PHP_EXECUTABLE"; then grepext=`$PHP_EXECUTABLE -m | $EGREP ^$extname\$` if test "$grepext" = "$extname"; then [PHP_PQ_HAVE_EXT_]translit($1,a-z_-,A-Z__)=1 AC_MSG_RESULT([yes]) $2 else [PHP_PQ_HAVE_EXT_]translit($1,a-z_-,A-Z__)= AC_MSG_RESULT([no]) $3 fi else [PHP_PQ_HAVE_EXT_]translit($1,a-z_-,A-Z__)= AC_MSG_RESULT([no]) $3 fi ]) PQ_SRC="\ src/php_pq_module.c\ src/php_pq_misc.c\ src/php_pq_callback.c\ src/php_pq_object.c\ src/php_pq_params.c\ src/php_pqcancel.c\ src/php_pqconn.c\ src/php_pqconn_event.c\ src/php_pqcopy.c\ src/php_pqexc.c\ src/php_pqlob.c\ src/php_pqres.c\ src/php_pqstm.c\ src/php_pqtxn.c\ src/php_pqtypes.c\ src/php_pqcur.c\ " PHP_NEW_EXTENSION(pq, $PQ_SRC, $ext_shared) PHP_ADD_BUILD_DIR($ext_builddir/src) PHP_ADD_INCLUDE($ext_srcdir/src) PQ_HAVE_PHP_EXT([raphf], [ AC_MSG_CHECKING([for php_raphf.h]) PQ_EXT_RAPHF_INCDIR= for i in `echo $INCLUDES | $SED -e's/-I//g'` $abs_srcdir ../raphf; do if test -d $i; then if test -f $i/php_raphf.h; then PQ_EXT_RAPHF_INCDIR=$i break elif test -f $i/ext/raphf/php_raphf.h; then PQ_EXT_RAPHF_INCDIR=$i/ext/raphf break fi fi done if test "x$PQ_EXT_RAPHF_INCDIR" = "x"; then AC_MSG_ERROR([not found]) else AC_MSG_RESULT([$PQ_EXT_RAPHF_INCDIR]) AC_DEFINE([PHP_PQ_HAVE_PHP_RAPHF_H], [1], [Have ext/raphf support]) PHP_ADD_INCLUDE([$PQ_EXT_RAPHF_INCDIR]) fi ], [ AC_MSG_ERROR([Please install pecl/raphf and activate extension=raphf.$SHLIB_DL_SUFFIX_NAME in your php.ini]) ]) PHP_ADD_EXTENSION_DEP(pq, raphf, true) PQ_HAVE_PHP_EXT([json], [ AC_MSG_CHECKING([for php_json.h]) PQ_EXT_JSON_INCDIR= for i in `echo $INCLUDES | $SED -e's/-I//g'` $abs_srcdir ../json ../jsonc ../jsond; do if test -d $i; then if test -f $i/php_json.h; then PQ_EXT_JSON_INCDIR=$i break elif test -f $i/ext/json/php_json.h; then PQ_EXT_JSON_INCDIR=$i/ext/json break fi fi done if test "x$PQ_EXT_JSON_INCDIR" = "x"; then AC_MSG_ERROR([not found]) else AC_MSG_RESULT([$PQ_EXT_JSON_INCDIR]) AC_DEFINE([PHP_PQ_HAVE_PHP_JSON_H], [1], [Have ext/json support]) PHP_ADD_INCLUDE([$PQ_EXT_JSON_INCDIR]) fi ]) fi /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifndef PHP_PQ_H #define PHP_PQ_H #define PHP_PQ_VERSION "0.6.0dev" #ifdef PHP_WIN32 # define PHP_PQ_API __declspec(dllexport) #elif defined(__GNUC__) && __GNUC__ >= 4 # define PHP_PQ_API extern __attribute__ ((visibility("default"))) #else # define PHP_PQ_API extern #endif extern int pq_module_number; extern zend_module_entry pq_module_entry; #define phpext_pq_ptr &pq_module_entry #ifdef ZTS # include "TSRM.h" # define TSRMLS_DF(d) TSRMLS_D = (d)->ts # define TSRMLS_CF(d) (d)->ts = TSRMLS_C #else # define TSRMLS_DF(d) # define TSRMLS_CF(d) #endif #endif /* PHP_PQ_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ #!/usr/bin/awk -f BEGIN { printf "#ifndef PHP_PQ_TYPE\n" printf "# define PHP_PQ_TYPE(t,o)\n" printf "#endif\n" } END { printf "#ifndef PHP_PQ_TYPE_IS_ARRAY\n" printf "# define PHP_PQ_TYPE_IS_ARRAY(oid) (\\\n\t\t0 \\\n" for (oid in arrays) { printf "\t||\t((oid) == %d) \\\n", oid } printf ")\n#endif\n" printf "#ifndef PHP_PQ_TYPE_OF_ARRAY\n" printf "# define PHP_PQ_TYPE_OF_ARRAY(oid) (" for (oid in arrays) { printf "\\\n\t(oid) == %d ? %s : ", oid, arrays[oid] } printf "0 \\\n)\n#endif\n" } /^DATA/ { oid = $4 name = toupper($6) atypoid = $17 if (sub("^_", "", name)) { arrays[oid] = atypoid name = name "ARRAY" } printf "#ifndef PHP_PQ_OID_%s\n", name printf "# define PHP_PQ_OID_%s %d\n", name, oid printf "#endif\n" printf "PHP_PQ_TYPE(\"%s\", %d)\n", name, oid } #ifndef PHP_PQ_TYPE # define PHP_PQ_TYPE(t,o) #endif #ifndef PHP_PQ_OID_BOOL # define PHP_PQ_OID_BOOL 16 #endif PHP_PQ_TYPE("BOOL", 16) #ifndef PHP_PQ_OID_BYTEA # define PHP_PQ_OID_BYTEA 17 #endif PHP_PQ_TYPE("BYTEA", 17) #ifndef PHP_PQ_OID_CHAR # define PHP_PQ_OID_CHAR 18 #endif PHP_PQ_TYPE("CHAR", 18) #ifndef PHP_PQ_OID_NAME # define PHP_PQ_OID_NAME 19 #endif PHP_PQ_TYPE("NAME", 19) #ifndef PHP_PQ_OID_INT8 # define PHP_PQ_OID_INT8 20 #endif PHP_PQ_TYPE("INT8", 20) #ifndef PHP_PQ_OID_INT2 # define PHP_PQ_OID_INT2 21 #endif PHP_PQ_TYPE("INT2", 21) #ifndef PHP_PQ_OID_INT2VECTOR # define PHP_PQ_OID_INT2VECTOR 22 #endif PHP_PQ_TYPE("INT2VECTOR", 22) #ifndef PHP_PQ_OID_INT4 # define PHP_PQ_OID_INT4 23 #endif PHP_PQ_TYPE("INT4", 23) #ifndef PHP_PQ_OID_REGPROC # define PHP_PQ_OID_REGPROC 24 #endif PHP_PQ_TYPE("REGPROC", 24) #ifndef PHP_PQ_OID_TEXT # define PHP_PQ_OID_TEXT 25 #endif PHP_PQ_TYPE("TEXT", 25) #ifndef PHP_PQ_OID_OID # define PHP_PQ_OID_OID 26 #endif PHP_PQ_TYPE("OID", 26) #ifndef PHP_PQ_OID_TID # define PHP_PQ_OID_TID 27 #endif PHP_PQ_TYPE("TID", 27) #ifndef PHP_PQ_OID_XID # define PHP_PQ_OID_XID 28 #endif PHP_PQ_TYPE("XID", 28) #ifndef PHP_PQ_OID_CID # define PHP_PQ_OID_CID 29 #endif PHP_PQ_TYPE("CID", 29) #ifndef PHP_PQ_OID_OIDVECTOR # define PHP_PQ_OID_OIDVECTOR 30 #endif PHP_PQ_TYPE("OIDVECTOR", 30) #ifndef PHP_PQ_OID_PG_TYPE # define PHP_PQ_OID_PG_TYPE 71 #endif PHP_PQ_TYPE("PG_TYPE", 71) #ifndef PHP_PQ_OID_PG_ATTRIBUTE # define PHP_PQ_OID_PG_ATTRIBUTE 75 #endif PHP_PQ_TYPE("PG_ATTRIBUTE", 75) #ifndef PHP_PQ_OID_PG_PROC # define PHP_PQ_OID_PG_PROC 81 #endif PHP_PQ_TYPE("PG_PROC", 81) #ifndef PHP_PQ_OID_PG_CLASS # define PHP_PQ_OID_PG_CLASS 83 #endif PHP_PQ_TYPE("PG_CLASS", 83) #ifndef PHP_PQ_OID_JSON # define PHP_PQ_OID_JSON 114 #endif PHP_PQ_TYPE("JSON", 114) #ifndef PHP_PQ_OID_XML # define PHP_PQ_OID_XML 142 #endif PHP_PQ_TYPE("XML", 142) #ifndef PHP_PQ_OID_XMLARRAY # define PHP_PQ_OID_XMLARRAY 143 #endif PHP_PQ_TYPE("XMLARRAY", 143) #ifndef PHP_PQ_OID_JSONARRAY # define PHP_PQ_OID_JSONARRAY 199 #endif PHP_PQ_TYPE("JSONARRAY", 199) #ifndef PHP_PQ_OID_PG_NODE_TREE # define PHP_PQ_OID_PG_NODE_TREE 194 #endif PHP_PQ_TYPE("PG_NODE_TREE", 194) #ifndef PHP_PQ_OID_SMGR # define PHP_PQ_OID_SMGR 210 #endif PHP_PQ_TYPE("SMGR", 210) #ifndef PHP_PQ_OID_POINT # define PHP_PQ_OID_POINT 600 #endif PHP_PQ_TYPE("POINT", 600) #ifndef PHP_PQ_OID_LSEG # define PHP_PQ_OID_LSEG 601 #endif PHP_PQ_TYPE("LSEG", 601) #ifndef PHP_PQ_OID_PATH # define PHP_PQ_OID_PATH 602 #endif PHP_PQ_TYPE("PATH", 602) #ifndef PHP_PQ_OID_BOX # define PHP_PQ_OID_BOX 603 #endif PHP_PQ_TYPE("BOX", 603) #ifndef PHP_PQ_OID_POLYGON # define PHP_PQ_OID_POLYGON 604 #endif PHP_PQ_TYPE("POLYGON", 604) #ifndef PHP_PQ_OID_LINE # define PHP_PQ_OID_LINE 628 #endif PHP_PQ_TYPE("LINE", 628) #ifndef PHP_PQ_OID_LINEARRAY # define PHP_PQ_OID_LINEARRAY 629 #endif PHP_PQ_TYPE("LINEARRAY", 629) #ifndef PHP_PQ_OID_FLOAT4 # define PHP_PQ_OID_FLOAT4 700 #endif PHP_PQ_TYPE("FLOAT4", 700) #ifndef PHP_PQ_OID_FLOAT8 # define PHP_PQ_OID_FLOAT8 701 #endif PHP_PQ_TYPE("FLOAT8", 701) #ifndef PHP_PQ_OID_ABSTIME # define PHP_PQ_OID_ABSTIME 702 #endif PHP_PQ_TYPE("ABSTIME", 702) #ifndef PHP_PQ_OID_RELTIME # define PHP_PQ_OID_RELTIME 703 #endif PHP_PQ_TYPE("RELTIME", 703) #ifndef PHP_PQ_OID_TINTERVAL # define PHP_PQ_OID_TINTERVAL 704 #endif PHP_PQ_TYPE("TINTERVAL", 704) #ifndef PHP_PQ_OID_UNKNOWN # define PHP_PQ_OID_UNKNOWN 705 #endif PHP_PQ_TYPE("UNKNOWN", 705) #ifndef PHP_PQ_OID_CIRCLE # define PHP_PQ_OID_CIRCLE 718 #endif PHP_PQ_TYPE("CIRCLE", 718) #ifndef PHP_PQ_OID_CIRCLEARRAY # define PHP_PQ_OID_CIRCLEARRAY 719 #endif PHP_PQ_TYPE("CIRCLEARRAY", 719) #ifndef PHP_PQ_OID_MONEY # define PHP_PQ_OID_MONEY 790 #endif PHP_PQ_TYPE("MONEY", 790) #ifndef PHP_PQ_OID_MONEYARRAY # define PHP_PQ_OID_MONEYARRAY 791 #endif PHP_PQ_TYPE("MONEYARRAY", 791) #ifndef PHP_PQ_OID_MACADDR # define PHP_PQ_OID_MACADDR 829 #endif PHP_PQ_TYPE("MACADDR", 829) #ifndef PHP_PQ_OID_INET # define PHP_PQ_OID_INET 869 #endif PHP_PQ_TYPE("INET", 869) #ifndef PHP_PQ_OID_CIDR # define PHP_PQ_OID_CIDR 650 #endif PHP_PQ_TYPE("CIDR", 650) #ifndef PHP_PQ_OID_BOOLARRAY # define PHP_PQ_OID_BOOLARRAY 1000 #endif PHP_PQ_TYPE("BOOLARRAY", 1000) #ifndef PHP_PQ_OID_BYTEAARRAY # define PHP_PQ_OID_BYTEAARRAY 1001 #endif PHP_PQ_TYPE("BYTEAARRAY", 1001) #ifndef PHP_PQ_OID_CHARARRAY # define PHP_PQ_OID_CHARARRAY 1002 #endif PHP_PQ_TYPE("CHARARRAY", 1002) #ifndef PHP_PQ_OID_NAMEARRAY # define PHP_PQ_OID_NAMEARRAY 1003 #endif PHP_PQ_TYPE("NAMEARRAY", 1003) #ifndef PHP_PQ_OID_INT2ARRAY # define PHP_PQ_OID_INT2ARRAY 1005 #endif PHP_PQ_TYPE("INT2ARRAY", 1005) #ifndef PHP_PQ_OID_INT2VECTORARRAY # define PHP_PQ_OID_INT2VECTORARRAY 1006 #endif PHP_PQ_TYPE("INT2VECTORARRAY", 1006) #ifndef PHP_PQ_OID_INT4ARRAY # define PHP_PQ_OID_INT4ARRAY 1007 #endif PHP_PQ_TYPE("INT4ARRAY", 1007) #ifndef PHP_PQ_OID_REGPROCARRAY # define PHP_PQ_OID_REGPROCARRAY 1008 #endif PHP_PQ_TYPE("REGPROCARRAY", 1008) #ifndef PHP_PQ_OID_TEXTARRAY # define PHP_PQ_OID_TEXTARRAY 1009 #endif PHP_PQ_TYPE("TEXTARRAY", 1009) #ifndef PHP_PQ_OID_OIDARRAY # define PHP_PQ_OID_OIDARRAY 1028 #endif PHP_PQ_TYPE("OIDARRAY", 1028) #ifndef PHP_PQ_OID_TIDARRAY # define PHP_PQ_OID_TIDARRAY 1010 #endif PHP_PQ_TYPE("TIDARRAY", 1010) #ifndef PHP_PQ_OID_XIDARRAY # define PHP_PQ_OID_XIDARRAY 1011 #endif PHP_PQ_TYPE("XIDARRAY", 1011) #ifndef PHP_PQ_OID_CIDARRAY # define PHP_PQ_OID_CIDARRAY 1012 #endif PHP_PQ_TYPE("CIDARRAY", 1012) #ifndef PHP_PQ_OID_OIDVECTORARRAY # define PHP_PQ_OID_OIDVECTORARRAY 1013 #endif PHP_PQ_TYPE("OIDVECTORARRAY", 1013) #ifndef PHP_PQ_OID_BPCHARARRAY # define PHP_PQ_OID_BPCHARARRAY 1014 #endif PHP_PQ_TYPE("BPCHARARRAY", 1014) #ifndef PHP_PQ_OID_VARCHARARRAY # define PHP_PQ_OID_VARCHARARRAY 1015 #endif PHP_PQ_TYPE("VARCHARARRAY", 1015) #ifndef PHP_PQ_OID_INT8ARRAY # define PHP_PQ_OID_INT8ARRAY 1016 #endif PHP_PQ_TYPE("INT8ARRAY", 1016) #ifndef PHP_PQ_OID_POINTARRAY # define PHP_PQ_OID_POINTARRAY 1017 #endif PHP_PQ_TYPE("POINTARRAY", 1017) #ifndef PHP_PQ_OID_LSEGARRAY # define PHP_PQ_OID_LSEGARRAY 1018 #endif PHP_PQ_TYPE("LSEGARRAY", 1018) #ifndef PHP_PQ_OID_PATHARRAY # define PHP_PQ_OID_PATHARRAY 1019 #endif PHP_PQ_TYPE("PATHARRAY", 1019) #ifndef PHP_PQ_OID_BOXARRAY # define PHP_PQ_OID_BOXARRAY 1020 #endif PHP_PQ_TYPE("BOXARRAY", 1020) #ifndef PHP_PQ_OID_FLOAT4ARRAY # define PHP_PQ_OID_FLOAT4ARRAY 1021 #endif PHP_PQ_TYPE("FLOAT4ARRAY", 1021) #ifndef PHP_PQ_OID_FLOAT8ARRAY # define PHP_PQ_OID_FLOAT8ARRAY 1022 #endif PHP_PQ_TYPE("FLOAT8ARRAY", 1022) #ifndef PHP_PQ_OID_ABSTIMEARRAY # define PHP_PQ_OID_ABSTIMEARRAY 1023 #endif PHP_PQ_TYPE("ABSTIMEARRAY", 1023) #ifndef PHP_PQ_OID_RELTIMEARRAY # define PHP_PQ_OID_RELTIMEARRAY 1024 #endif PHP_PQ_TYPE("RELTIMEARRAY", 1024) #ifndef PHP_PQ_OID_TINTERVALARRAY # define PHP_PQ_OID_TINTERVALARRAY 1025 #endif PHP_PQ_TYPE("TINTERVALARRAY", 1025) #ifndef PHP_PQ_OID_POLYGONARRAY # define PHP_PQ_OID_POLYGONARRAY 1027 #endif PHP_PQ_TYPE("POLYGONARRAY", 1027) #ifndef PHP_PQ_OID_ACLITEM # define PHP_PQ_OID_ACLITEM 1033 #endif PHP_PQ_TYPE("ACLITEM", 1033) #ifndef PHP_PQ_OID_ACLITEMARRAY # define PHP_PQ_OID_ACLITEMARRAY 1034 #endif PHP_PQ_TYPE("ACLITEMARRAY", 1034) #ifndef PHP_PQ_OID_MACADDRARRAY # define PHP_PQ_OID_MACADDRARRAY 1040 #endif PHP_PQ_TYPE("MACADDRARRAY", 1040) #ifndef PHP_PQ_OID_INETARRAY # define PHP_PQ_OID_INETARRAY 1041 #endif PHP_PQ_TYPE("INETARRAY", 1041) #ifndef PHP_PQ_OID_CIDRARRAY # define PHP_PQ_OID_CIDRARRAY 651 #endif PHP_PQ_TYPE("CIDRARRAY", 651) #ifndef PHP_PQ_OID_CSTRINGARRAY # define PHP_PQ_OID_CSTRINGARRAY 1263 #endif PHP_PQ_TYPE("CSTRINGARRAY", 1263) #ifndef PHP_PQ_OID_BPCHAR # define PHP_PQ_OID_BPCHAR 1042 #endif PHP_PQ_TYPE("BPCHAR", 1042) #ifndef PHP_PQ_OID_VARCHAR # define PHP_PQ_OID_VARCHAR 1043 #endif PHP_PQ_TYPE("VARCHAR", 1043) #ifndef PHP_PQ_OID_DATE # define PHP_PQ_OID_DATE 1082 #endif PHP_PQ_TYPE("DATE", 1082) #ifndef PHP_PQ_OID_TIME # define PHP_PQ_OID_TIME 1083 #endif PHP_PQ_TYPE("TIME", 1083) #ifndef PHP_PQ_OID_TIMESTAMP # define PHP_PQ_OID_TIMESTAMP 1114 #endif PHP_PQ_TYPE("TIMESTAMP", 1114) #ifndef PHP_PQ_OID_TIMESTAMPARRAY # define PHP_PQ_OID_TIMESTAMPARRAY 1115 #endif PHP_PQ_TYPE("TIMESTAMPARRAY", 1115) #ifndef PHP_PQ_OID_DATEARRAY # define PHP_PQ_OID_DATEARRAY 1182 #endif PHP_PQ_TYPE("DATEARRAY", 1182) #ifndef PHP_PQ_OID_TIMEARRAY # define PHP_PQ_OID_TIMEARRAY 1183 #endif PHP_PQ_TYPE("TIMEARRAY", 1183) #ifndef PHP_PQ_OID_TIMESTAMPTZ # define PHP_PQ_OID_TIMESTAMPTZ 1184 #endif PHP_PQ_TYPE("TIMESTAMPTZ", 1184) #ifndef PHP_PQ_OID_TIMESTAMPTZARRAY # define PHP_PQ_OID_TIMESTAMPTZARRAY 1185 #endif PHP_PQ_TYPE("TIMESTAMPTZARRAY", 1185) #ifndef PHP_PQ_OID_INTERVAL # define PHP_PQ_OID_INTERVAL 1186 #endif PHP_PQ_TYPE("INTERVAL", 1186) #ifndef PHP_PQ_OID_INTERVALARRAY # define PHP_PQ_OID_INTERVALARRAY 1187 #endif PHP_PQ_TYPE("INTERVALARRAY", 1187) #ifndef PHP_PQ_OID_NUMERICARRAY # define PHP_PQ_OID_NUMERICARRAY 1231 #endif PHP_PQ_TYPE("NUMERICARRAY", 1231) #ifndef PHP_PQ_OID_TIMETZ # define PHP_PQ_OID_TIMETZ 1266 #endif PHP_PQ_TYPE("TIMETZ", 1266) #ifndef PHP_PQ_OID_TIMETZARRAY # define PHP_PQ_OID_TIMETZARRAY 1270 #endif PHP_PQ_TYPE("TIMETZARRAY", 1270) #ifndef PHP_PQ_OID_BIT # define PHP_PQ_OID_BIT 1560 #endif PHP_PQ_TYPE("BIT", 1560) #ifndef PHP_PQ_OID_BITARRAY # define PHP_PQ_OID_BITARRAY 1561 #endif PHP_PQ_TYPE("BITARRAY", 1561) #ifndef PHP_PQ_OID_VARBIT # define PHP_PQ_OID_VARBIT 1562 #endif PHP_PQ_TYPE("VARBIT", 1562) #ifndef PHP_PQ_OID_VARBITARRAY # define PHP_PQ_OID_VARBITARRAY 1563 #endif PHP_PQ_TYPE("VARBITARRAY", 1563) #ifndef PHP_PQ_OID_NUMERIC # define PHP_PQ_OID_NUMERIC 1700 #endif PHP_PQ_TYPE("NUMERIC", 1700) #ifndef PHP_PQ_OID_REFCURSOR # define PHP_PQ_OID_REFCURSOR 1790 #endif PHP_PQ_TYPE("REFCURSOR", 1790) #ifndef PHP_PQ_OID_REFCURSORARRAY # define PHP_PQ_OID_REFCURSORARRAY 2201 #endif PHP_PQ_TYPE("REFCURSORARRAY", 2201) #ifndef PHP_PQ_OID_REGPROCEDURE # define PHP_PQ_OID_REGPROCEDURE 2202 #endif PHP_PQ_TYPE("REGPROCEDURE", 2202) #ifndef PHP_PQ_OID_REGOPER # define PHP_PQ_OID_REGOPER 2203 #endif PHP_PQ_TYPE("REGOPER", 2203) #ifndef PHP_PQ_OID_REGOPERATOR # define PHP_PQ_OID_REGOPERATOR 2204 #endif PHP_PQ_TYPE("REGOPERATOR", 2204) #ifndef PHP_PQ_OID_REGCLASS # define PHP_PQ_OID_REGCLASS 2205 #endif PHP_PQ_TYPE("REGCLASS", 2205) #ifndef PHP_PQ_OID_REGTYPE # define PHP_PQ_OID_REGTYPE 2206 #endif PHP_PQ_TYPE("REGTYPE", 2206) #ifndef PHP_PQ_OID_REGPROCEDUREARRAY # define PHP_PQ_OID_REGPROCEDUREARRAY 2207 #endif PHP_PQ_TYPE("REGPROCEDUREARRAY", 2207) #ifndef PHP_PQ_OID_REGOPERARRAY # define PHP_PQ_OID_REGOPERARRAY 2208 #endif PHP_PQ_TYPE("REGOPERARRAY", 2208) #ifndef PHP_PQ_OID_REGOPERATORARRAY # define PHP_PQ_OID_REGOPERATORARRAY 2209 #endif PHP_PQ_TYPE("REGOPERATORARRAY", 2209) #ifndef PHP_PQ_OID_REGCLASSARRAY # define PHP_PQ_OID_REGCLASSARRAY 2210 #endif PHP_PQ_TYPE("REGCLASSARRAY", 2210) #ifndef PHP_PQ_OID_REGTYPEARRAY # define PHP_PQ_OID_REGTYPEARRAY 2211 #endif PHP_PQ_TYPE("REGTYPEARRAY", 2211) #ifndef PHP_PQ_OID_UUID # define PHP_PQ_OID_UUID 2950 #endif PHP_PQ_TYPE("UUID", 2950) #ifndef PHP_PQ_OID_UUIDARRAY # define PHP_PQ_OID_UUIDARRAY 2951 #endif PHP_PQ_TYPE("UUIDARRAY", 2951) #ifndef PHP_PQ_OID_PG_LSN # define PHP_PQ_OID_PG_LSN 3220 #endif PHP_PQ_TYPE("PG_LSN", 3220) #ifndef PHP_PQ_OID_PG_LSNARRAY # define PHP_PQ_OID_PG_LSNARRAY 3221 #endif PHP_PQ_TYPE("PG_LSNARRAY", 3221) #ifndef PHP_PQ_OID_TSVECTOR # define PHP_PQ_OID_TSVECTOR 3614 #endif PHP_PQ_TYPE("TSVECTOR", 3614) #ifndef PHP_PQ_OID_GTSVECTOR # define PHP_PQ_OID_GTSVECTOR 3642 #endif PHP_PQ_TYPE("GTSVECTOR", 3642) #ifndef PHP_PQ_OID_TSQUERY # define PHP_PQ_OID_TSQUERY 3615 #endif PHP_PQ_TYPE("TSQUERY", 3615) #ifndef PHP_PQ_OID_REGCONFIG # define PHP_PQ_OID_REGCONFIG 3734 #endif PHP_PQ_TYPE("REGCONFIG", 3734) #ifndef PHP_PQ_OID_REGDICTIONARY # define PHP_PQ_OID_REGDICTIONARY 3769 #endif PHP_PQ_TYPE("REGDICTIONARY", 3769) #ifndef PHP_PQ_OID_TSVECTORARRAY # define PHP_PQ_OID_TSVECTORARRAY 3643 #endif PHP_PQ_TYPE("TSVECTORARRAY", 3643) #ifndef PHP_PQ_OID_GTSVECTORARRAY # define PHP_PQ_OID_GTSVECTORARRAY 3644 #endif PHP_PQ_TYPE("GTSVECTORARRAY", 3644) #ifndef PHP_PQ_OID_TSQUERYARRAY # define PHP_PQ_OID_TSQUERYARRAY 3645 #endif PHP_PQ_TYPE("TSQUERYARRAY", 3645) #ifndef PHP_PQ_OID_REGCONFIGARRAY # define PHP_PQ_OID_REGCONFIGARRAY 3735 #endif PHP_PQ_TYPE("REGCONFIGARRAY", 3735) #ifndef PHP_PQ_OID_REGDICTIONARYARRAY # define PHP_PQ_OID_REGDICTIONARYARRAY 3770 #endif PHP_PQ_TYPE("REGDICTIONARYARRAY", 3770) #ifndef PHP_PQ_OID_JSONB # define PHP_PQ_OID_JSONB 3802 #endif PHP_PQ_TYPE("JSONB", 3802) #ifndef PHP_PQ_OID_JSONBARRAY # define PHP_PQ_OID_JSONBARRAY 3807 #endif PHP_PQ_TYPE("JSONBARRAY", 3807) #ifndef PHP_PQ_OID_TXID_SNAPSHOT # define PHP_PQ_OID_TXID_SNAPSHOT 2970 #endif PHP_PQ_TYPE("TXID_SNAPSHOT", 2970) #ifndef PHP_PQ_OID_TXID_SNAPSHOTARRAY # define PHP_PQ_OID_TXID_SNAPSHOTARRAY 2949 #endif PHP_PQ_TYPE("TXID_SNAPSHOTARRAY", 2949) #ifndef PHP_PQ_OID_INT4RANGE # define PHP_PQ_OID_INT4RANGE 3904 #endif PHP_PQ_TYPE("INT4RANGE", 3904) #ifndef PHP_PQ_OID_INT4RANGEARRAY # define PHP_PQ_OID_INT4RANGEARRAY 3905 #endif PHP_PQ_TYPE("INT4RANGEARRAY", 3905) #ifndef PHP_PQ_OID_NUMRANGE # define PHP_PQ_OID_NUMRANGE 3906 #endif PHP_PQ_TYPE("NUMRANGE", 3906) #ifndef PHP_PQ_OID_NUMRANGEARRAY # define PHP_PQ_OID_NUMRANGEARRAY 3907 #endif PHP_PQ_TYPE("NUMRANGEARRAY", 3907) #ifndef PHP_PQ_OID_TSRANGE # define PHP_PQ_OID_TSRANGE 3908 #endif PHP_PQ_TYPE("TSRANGE", 3908) #ifndef PHP_PQ_OID_TSRANGEARRAY # define PHP_PQ_OID_TSRANGEARRAY 3909 #endif PHP_PQ_TYPE("TSRANGEARRAY", 3909) #ifndef PHP_PQ_OID_TSTZRANGE # define PHP_PQ_OID_TSTZRANGE 3910 #endif PHP_PQ_TYPE("TSTZRANGE", 3910) #ifndef PHP_PQ_OID_TSTZRANGEARRAY # define PHP_PQ_OID_TSTZRANGEARRAY 3911 #endif PHP_PQ_TYPE("TSTZRANGEARRAY", 3911) #ifndef PHP_PQ_OID_DATERANGE # define PHP_PQ_OID_DATERANGE 3912 #endif PHP_PQ_TYPE("DATERANGE", 3912) #ifndef PHP_PQ_OID_DATERANGEARRAY # define PHP_PQ_OID_DATERANGEARRAY 3913 #endif PHP_PQ_TYPE("DATERANGEARRAY", 3913) #ifndef PHP_PQ_OID_INT8RANGE # define PHP_PQ_OID_INT8RANGE 3926 #endif PHP_PQ_TYPE("INT8RANGE", 3926) #ifndef PHP_PQ_OID_INT8RANGEARRAY # define PHP_PQ_OID_INT8RANGEARRAY 3927 #endif PHP_PQ_TYPE("INT8RANGEARRAY", 3927) #ifndef PHP_PQ_OID_RECORD # define PHP_PQ_OID_RECORD 2249 #endif PHP_PQ_TYPE("RECORD", 2249) #ifndef PHP_PQ_OID_RECORDARRAY # define PHP_PQ_OID_RECORDARRAY 2287 #endif PHP_PQ_TYPE("RECORDARRAY", 2287) #ifndef PHP_PQ_OID_CSTRING # define PHP_PQ_OID_CSTRING 2275 #endif PHP_PQ_TYPE("CSTRING", 2275) #ifndef PHP_PQ_OID_ANY # define PHP_PQ_OID_ANY 2276 #endif PHP_PQ_TYPE("ANY", 2276) #ifndef PHP_PQ_OID_ANYARRAY # define PHP_PQ_OID_ANYARRAY 2277 #endif PHP_PQ_TYPE("ANYARRAY", 2277) #ifndef PHP_PQ_OID_VOID # define PHP_PQ_OID_VOID 2278 #endif PHP_PQ_TYPE("VOID", 2278) #ifndef PHP_PQ_OID_TRIGGER # define PHP_PQ_OID_TRIGGER 2279 #endif PHP_PQ_TYPE("TRIGGER", 2279) #ifndef PHP_PQ_OID_EVENT_TRIGGER # define PHP_PQ_OID_EVENT_TRIGGER 3838 #endif PHP_PQ_TYPE("EVENT_TRIGGER", 3838) #ifndef PHP_PQ_OID_LANGUAGE_HANDLER # define PHP_PQ_OID_LANGUAGE_HANDLER 2280 #endif PHP_PQ_TYPE("LANGUAGE_HANDLER", 2280) #ifndef PHP_PQ_OID_INTERNAL # define PHP_PQ_OID_INTERNAL 2281 #endif PHP_PQ_TYPE("INTERNAL", 2281) #ifndef PHP_PQ_OID_OPAQUE # define PHP_PQ_OID_OPAQUE 2282 #endif PHP_PQ_TYPE("OPAQUE", 2282) #ifndef PHP_PQ_OID_ANYELEMENT # define PHP_PQ_OID_ANYELEMENT 2283 #endif PHP_PQ_TYPE("ANYELEMENT", 2283) #ifndef PHP_PQ_OID_ANYNONARRAY # define PHP_PQ_OID_ANYNONARRAY 2776 #endif PHP_PQ_TYPE("ANYNONARRAY", 2776) #ifndef PHP_PQ_OID_ANYENUM # define PHP_PQ_OID_ANYENUM 3500 #endif PHP_PQ_TYPE("ANYENUM", 3500) #ifndef PHP_PQ_OID_FDW_HANDLER # define PHP_PQ_OID_FDW_HANDLER 3115 #endif PHP_PQ_TYPE("FDW_HANDLER", 3115) #ifndef PHP_PQ_OID_ANYRANGE # define PHP_PQ_OID_ANYRANGE 3831 #endif PHP_PQ_TYPE("ANYRANGE", 3831) #ifndef PHP_PQ_TYPE_IS_ARRAY # define PHP_PQ_TYPE_IS_ARRAY(oid) (\ 0 \ || ((oid) == 143) \ || ((oid) == 199) \ || ((oid) == 629) \ || ((oid) == 651) \ || ((oid) == 719) \ || ((oid) == 791) \ || ((oid) == 1000) \ || ((oid) == 1001) \ || ((oid) == 1002) \ || ((oid) == 1003) \ || ((oid) == 1005) \ || ((oid) == 1006) \ || ((oid) == 1007) \ || ((oid) == 1008) \ || ((oid) == 1009) \ || ((oid) == 1010) \ || ((oid) == 1011) \ || ((oid) == 1012) \ || ((oid) == 1013) \ || ((oid) == 1014) \ || ((oid) == 1015) \ || ((oid) == 1016) \ || ((oid) == 1017) \ || ((oid) == 1018) \ || ((oid) == 1019) \ || ((oid) == 1020) \ || ((oid) == 1021) \ || ((oid) == 1022) \ || ((oid) == 1023) \ || ((oid) == 1024) \ || ((oid) == 1025) \ || ((oid) == 1027) \ || ((oid) == 1028) \ || ((oid) == 1034) \ || ((oid) == 1040) \ || ((oid) == 1041) \ || ((oid) == 1115) \ || ((oid) == 1182) \ || ((oid) == 1183) \ || ((oid) == 1185) \ || ((oid) == 1187) \ || ((oid) == 1231) \ || ((oid) == 1263) \ || ((oid) == 1270) \ || ((oid) == 1561) \ || ((oid) == 1563) \ || ((oid) == 2201) \ || ((oid) == 2207) \ || ((oid) == 2208) \ || ((oid) == 2209) \ || ((oid) == 2210) \ || ((oid) == 2211) \ || ((oid) == 2287) \ || ((oid) == 2949) \ || ((oid) == 2951) \ || ((oid) == 3221) \ || ((oid) == 3643) \ || ((oid) == 3644) \ || ((oid) == 3645) \ || ((oid) == 3735) \ || ((oid) == 3770) \ || ((oid) == 3807) \ || ((oid) == 3905) \ || ((oid) == 3907) \ || ((oid) == 3909) \ || ((oid) == 3911) \ || ((oid) == 3913) \ || ((oid) == 3927) \ ) #endif #ifndef PHP_PQ_TYPE_OF_ARRAY # define PHP_PQ_TYPE_OF_ARRAY(oid) (\ (oid) == 143 ? 142 : \ (oid) == 199 ? 114 : \ (oid) == 629 ? 628 : \ (oid) == 651 ? 650 : \ (oid) == 719 ? 718 : \ (oid) == 791 ? 790 : \ (oid) == 1000 ? 16 : \ (oid) == 1001 ? 17 : \ (oid) == 1002 ? 18 : \ (oid) == 1003 ? 19 : \ (oid) == 1005 ? 21 : \ (oid) == 1006 ? 22 : \ (oid) == 1007 ? 23 : \ (oid) == 1008 ? 24 : \ (oid) == 1009 ? 25 : \ (oid) == 1010 ? 27 : \ (oid) == 1011 ? 28 : \ (oid) == 1012 ? 29 : \ (oid) == 1013 ? 30 : \ (oid) == 1014 ? 1042 : \ (oid) == 1015 ? 1043 : \ (oid) == 1016 ? 20 : \ (oid) == 1017 ? 600 : \ (oid) == 1018 ? 601 : \ (oid) == 1019 ? 602 : \ (oid) == 1020 ? 603 : \ (oid) == 1021 ? 700 : \ (oid) == 1022 ? 701 : \ (oid) == 1023 ? 702 : \ (oid) == 1024 ? 703 : \ (oid) == 1025 ? 704 : \ (oid) == 1027 ? 604 : \ (oid) == 1028 ? 26 : \ (oid) == 1034 ? 1033 : \ (oid) == 1040 ? 829 : \ (oid) == 1041 ? 869 : \ (oid) == 1115 ? 1114 : \ (oid) == 1182 ? 1082 : \ (oid) == 1183 ? 1083 : \ (oid) == 1185 ? 1184 : \ (oid) == 1187 ? 1186 : \ (oid) == 1231 ? 1700 : \ (oid) == 1263 ? 2275 : \ (oid) == 1270 ? 1266 : \ (oid) == 1561 ? 1560 : \ (oid) == 1563 ? 1562 : \ (oid) == 2201 ? 1790 : \ (oid) == 2207 ? 2202 : \ (oid) == 2208 ? 2203 : \ (oid) == 2209 ? 2204 : \ (oid) == 2210 ? 2205 : \ (oid) == 2211 ? 2206 : \ (oid) == 2287 ? 2249 : \ (oid) == 2949 ? 2970 : \ (oid) == 2951 ? 2950 : \ (oid) == 3221 ? 3220 : \ (oid) == 3643 ? 3614 : \ (oid) == 3644 ? 3642 : \ (oid) == 3645 ? 3615 : \ (oid) == 3735 ? 3734 : \ (oid) == 3770 ? 3769 : \ (oid) == 3807 ? 3802 : \ (oid) == 3905 ? 3904 : \ (oid) == 3907 ? 3906 : \ (oid) == 3909 ? 3908 : \ (oid) == 3911 ? 3910 : \ (oid) == 3913 ? 3912 : \ (oid) == 3927 ? 3926 : 0 \ ) #endif /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <php.h> #include <Zend/zend_closures.h> #include "php_pq_callback.h" void php_pq_callback_dtor(php_pq_callback_t *cb) { if (cb->recursion) { php_pq_callback_dtor(cb->recursion); efree(cb->recursion); cb->recursion = NULL; } if (cb->fci.size > 0) { zend_fcall_info_args_clear(&cb->fci, 1); zval_ptr_dtor(&cb->fci.function_name); if (cb->fci.object_ptr) { zval_ptr_dtor(&cb->fci.object_ptr); } cb->fci.size = 0; } } void php_pq_callback_addref(php_pq_callback_t *cb) { Z_ADDREF_P(cb->fci.function_name); if (cb->fci.object_ptr) { Z_ADDREF_P(cb->fci.object_ptr); } } zval *php_pq_callback_to_zval(php_pq_callback_t *cb) { zval *zcb; php_pq_callback_addref(cb); if (cb->fci.object_ptr) { MAKE_STD_ZVAL(zcb); array_init_size(zcb, 2); add_next_index_zval(zcb, cb->fci.object_ptr); add_next_index_zval(zcb, cb->fci.function_name); } else { zcb = cb->fci.function_name; } return zcb; } zend_bool php_pq_callback_is_locked(php_pq_callback_t *cb TSRMLS_DC) { if (cb->fci.size > 0 && Z_TYPE_P(cb->fci.function_name) == IS_OBJECT) { const zend_function *closure = zend_get_closure_method_def(cb->fci.function_name TSRMLS_CC); if (closure->type == ZEND_USER_FUNCTION) { zend_execute_data *ex = EG(current_execute_data); while (ex) { if (ex->op_array == &closure->op_array) { return 1; } ex = ex->prev_execute_data; } } } return 0; } void php_pq_callback_recurse(php_pq_callback_t *old, php_pq_callback_t *new TSRMLS_DC) { if (new && new->fci.size > 0 && php_pq_callback_is_locked(old TSRMLS_CC)) { new->recursion = emalloc(sizeof(*old)); memcpy(new->recursion, old, sizeof(*old)); } else if (new && new->fci.size > 0) { php_pq_callback_dtor(old); php_pq_callback_addref(new); memcpy(old, new, sizeof(*old)); new->fci.size = 0; } else { php_pq_callback_dtor(old); } } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifndef PHP_PQ_CALLBACK_H #define PHP_PQ_CALLBACK_H #include <Zend/zend_interfaces.h> typedef struct php_pq_callback { zend_fcall_info fci; zend_fcall_info_cache fcc; struct php_pq_callback *recursion; } php_pq_callback_t; extern void php_pq_callback_dtor(php_pq_callback_t *cb); extern void php_pq_callback_addref(php_pq_callback_t *cb); extern zval *php_pq_callback_to_zval(php_pq_callback_t *cb); extern zend_bool php_pq_callback_is_locked(php_pq_callback_t *cb TSRMLS_DC); extern void php_pq_callback_recurse(php_pq_callback_t *old, php_pq_callback_t *new TSRMLS_DC); #endif /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <php.h> #include <libpq-fe.h> #include "php_pq.h" #include "php_pq_misc.h" #include "php_pq_object.h" #include "php_pqexc.h" #include "php_pqcancel.h" zend_class_entry *php_pqcancel_class_entry; static zend_object_handlers php_pqcancel_object_handlers; static HashTable php_pqcancel_object_prophandlers; static void php_pqcancel_object_free(void *o TSRMLS_DC) { php_pqcancel_object_t *obj = o; #if DBG_GC fprintf(stderr, "FREE cancel(#%d) %p (conn(#%d): %p)\n", obj->zv.handle, obj, obj->intern->conn->zv.handle, obj->intern->conn); #endif if (obj->intern) { PQfreeCancel(obj->intern->cancel); php_pq_object_delref(obj->intern->conn TSRMLS_CC); efree(obj->intern); obj->intern = NULL; } zend_object_std_dtor((zend_object *) o TSRMLS_CC); efree(obj); } zend_object_value php_pqcancel_create_object_ex(zend_class_entry *ce, php_pqcancel_t *intern, php_pqcancel_object_t **ptr TSRMLS_DC) { php_pqcancel_object_t *o; o = ecalloc(1, sizeof(*o)); zend_object_std_init((zend_object *) o, ce TSRMLS_CC); object_properties_init((zend_object *) o, ce); o->prophandler = &php_pqcancel_object_prophandlers; if (ptr) { *ptr = o; } if (intern) { o->intern = intern; } o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqcancel_object_free, NULL TSRMLS_CC); o->zv.handlers = &php_pqcancel_object_handlers; return o->zv; } static zend_object_value php_pqcancel_create_object(zend_class_entry *class_type TSRMLS_DC) { return php_pqcancel_create_object_ex(class_type, NULL, NULL TSRMLS_CC); } static void php_pqcancel_object_read_connection(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqcancel_object_t *obj = o; php_pq_object_to_zval(obj->intern->conn, &return_value TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqcancel_construct, 0, 0, 1) ZEND_ARG_OBJ_INFO(0, connection, pq\\Connection, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqcancel, __construct) { zend_error_handling zeh; zval *zconn; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zconn, php_pqconn_class_entry); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *conn_obj = zend_object_store_get_object(zconn TSRMLS_CC); if (!conn_obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { PGcancel *cancel = PQgetCancel(conn_obj->intern->conn); if (!cancel) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to acquire cancel (%s)", PHP_PQerrorMessage(conn_obj->intern->conn)); } else { php_pqcancel_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); obj->intern = ecalloc(1, sizeof(*obj->intern)); obj->intern->cancel = cancel; php_pq_object_addref(conn_obj TSRMLS_CC); obj->intern->conn = conn_obj; } } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqcancel_cancel, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqcancel, cancel) { zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters_none(); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqcancel_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Cancel not initialized"); } else { char err[256] = {0}; if (!PQcancel(obj->intern->cancel, err, sizeof(err))) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to request cancellation (%s)", err); } } } } static zend_function_entry php_pqcancel_methods[] = { PHP_ME(pqcancel, __construct, ai_pqcancel_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) PHP_ME(pqcancel, cancel, ai_pqcancel_cancel, ZEND_ACC_PUBLIC) {0} }; PHP_MSHUTDOWN_FUNCTION(pqcancel) { zend_hash_destroy(&php_pqcancel_object_prophandlers); return SUCCESS; } PHP_MINIT_FUNCTION(pqcancel) { zend_class_entry ce = {0}; php_pq_object_prophandler_t ph = {0}; INIT_NS_CLASS_ENTRY(ce, "pq", "Cancel", php_pqcancel_methods); php_pqcancel_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC); php_pqcancel_class_entry->create_object = php_pqcancel_create_object; memcpy(&php_pqcancel_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); php_pqcancel_object_handlers.read_property = php_pq_object_read_prop; php_pqcancel_object_handlers.write_property = php_pq_object_write_prop; php_pqcancel_object_handlers.clone_obj = NULL; php_pqcancel_object_handlers.get_property_ptr_ptr = NULL; php_pqcancel_object_handlers.get_gc = NULL; php_pqcancel_object_handlers.get_properties = php_pq_object_properties; php_pqcancel_object_handlers.get_debug_info = php_pq_object_debug_info; zend_hash_init(&php_pqcancel_object_prophandlers, 1, NULL, NULL, 1); zend_declare_property_null(php_pqcancel_class_entry, ZEND_STRL("connection"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqcancel_object_read_connection; zend_hash_add(&php_pqcancel_object_prophandlers, "connection", sizeof("connection"), (void *) &ph, sizeof(ph), NULL); return SUCCESS; } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifndef PHP_PQCANCEL_H #define PHP_PQCANCEL_H #include "php_pqconn.h" typedef struct php_pqcancel { PGcancel *cancel; php_pqconn_object_t *conn; } php_pqcancel_t; typedef struct php_pqcancel_object { zend_object zo; zend_object_value zv; HashTable *prophandler; php_pqcancel_t *intern; } php_pqcancel_object_t; extern zend_class_entry *php_pqcancel_class_entry; extern zend_object_value php_pqcancel_create_object_ex(zend_class_entry *ce, php_pqcancel_t *intern, php_pqcancel_object_t **ptr TSRMLS_DC); extern PHP_MINIT_FUNCTION(pqcancel); extern PHP_MSHUTDOWN_FUNCTION(pqcancel); #endif /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <php.h> #define SMART_STR_PREALLOC 256 #include <ext/standard/php_smart_str.h> #include <libpq-events.h> #include <fnmatch.h> #include "php_pq.h" #include "php_pq_misc.h" #include "php_pq_object.h" #include "php_pqexc.h" #include "php_pqconn.h" #include "php_pqconn_event.h" #include "php_pqres.h" #include "php_pqstm.h" #include "php_pqtxn.h" #include "php_pqcur.h" zend_class_entry *php_pqconn_class_entry; static zend_object_handlers php_pqconn_object_handlers; static HashTable php_pqconn_object_prophandlers; /* static void php_pqconn_del_eventhandler(php_pqconn_object_t *obj, const char *type_str, size_t type_len, ulong id TSRMLS_DC) { zval **evhs; if (SUCCESS == zend_hash_find(&obj->intern->eventhandlers, type_str, type_len + 1, (void *) &evhs)) { zend_hash_index_del(Z_ARRVAL_PP(evhs), id); } } */ static ulong php_pqconn_add_eventhandler(php_pqconn_object_t *obj, const char *type_str, size_t type_len, php_pq_callback_t *cb TSRMLS_DC) { ulong h; HashTable *evhs; if (SUCCESS != zend_hash_find(&obj->intern->eventhandlers, type_str, type_len + 1, (void *) &evhs)) { HashTable evh; zend_hash_init(&evh, 1, NULL, (dtor_func_t) php_pq_callback_dtor, 0); zend_hash_add(&obj->intern->eventhandlers, type_str, type_len + 1, (void *) &evh, sizeof(evh), (void *) &evhs); } php_pq_callback_addref(cb); h = zend_hash_next_free_element(evhs); zend_hash_index_update(evhs, h, (void *) cb, sizeof(*cb), NULL); return h; } static void php_pqconn_object_free(void *o TSRMLS_DC) { php_pqconn_object_t *obj = o; #if DBG_GC fprintf(stderr, "FREE conn(#%d) %p\n", obj->zv.handle, obj); #endif if (obj->intern) { php_pq_callback_dtor(&obj->intern->onevent); php_resource_factory_handle_dtor(&obj->intern->factory, obj->intern->conn TSRMLS_CC); php_resource_factory_dtor(&obj->intern->factory); zend_hash_destroy(&obj->intern->listeners); zend_hash_destroy(&obj->intern->converters); zend_hash_destroy(&obj->intern->eventhandlers); efree(obj->intern); obj->intern = NULL; } zend_object_std_dtor((zend_object *) o TSRMLS_CC); efree(obj); } zend_object_value php_pqconn_create_object_ex(zend_class_entry *ce, php_pqconn_t *intern, php_pqconn_object_t **ptr TSRMLS_DC) { php_pqconn_object_t *o; o = ecalloc(1, sizeof(*o)); zend_object_std_init((zend_object *) o, ce TSRMLS_CC); object_properties_init((zend_object *) o, ce); o->prophandler = &php_pqconn_object_prophandlers; if (ptr) { *ptr = o; } if (intern) { o->intern = intern; } o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqconn_object_free, NULL TSRMLS_CC); o->zv.handlers = &php_pqconn_object_handlers; return o->zv; } static zend_object_value php_pqconn_create_object(zend_class_entry *class_type TSRMLS_DC) { return php_pqconn_create_object_ex(class_type, NULL, NULL TSRMLS_CC); } static void php_pqconn_object_read_status(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; RETVAL_LONG(PQstatus(obj->intern->conn)); } static void php_pqconn_object_read_transaction_status(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; RETVAL_LONG(PQtransactionStatus(obj->intern->conn)); } static void php_pqconn_object_read_error_message(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; char *error = PHP_PQerrorMessage(obj->intern->conn); if (error) { RETVAL_STRING(error, 1); } else { RETVAL_NULL(); } } static int apply_notify_listener(void *p, void *arg TSRMLS_DC) { php_pq_callback_t *listener = p; PGnotify *nfy = arg; zval *zpid, *zchannel, *zmessage; MAKE_STD_ZVAL(zpid); ZVAL_LONG(zpid, nfy->be_pid); MAKE_STD_ZVAL(zchannel); ZVAL_STRING(zchannel, nfy->relname, 1); MAKE_STD_ZVAL(zmessage); ZVAL_STRING(zmessage, nfy->extra, 1); zend_fcall_info_argn(&listener->fci TSRMLS_CC, 3, &zchannel, &zmessage, &zpid); zend_fcall_info_call(&listener->fci, &listener->fcc, NULL, NULL TSRMLS_CC); zval_ptr_dtor(&zchannel); zval_ptr_dtor(&zmessage); zval_ptr_dtor(&zpid); return ZEND_HASH_APPLY_KEEP; } static int apply_notify_listeners(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) { HashTable *listeners = p; PGnotify *nfy = va_arg(argv, PGnotify *); if (0 == fnmatch(key->arKey, nfy->relname, 0)) { zend_hash_apply_with_argument(listeners, apply_notify_listener, nfy TSRMLS_CC); } return ZEND_HASH_APPLY_KEEP; } void php_pqconn_notify_listeners(php_pqconn_object_t *obj TSRMLS_DC) { PGnotify *nfy; while ((nfy = PQnotifies(obj->intern->conn))) { zend_hash_apply_with_arguments(&obj->intern->listeners TSRMLS_CC, apply_notify_listeners, 1, nfy); PQfreemem(nfy); } } static void php_pqconn_object_read_busy(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; RETVAL_BOOL(PQisBusy(obj->intern->conn)); } static void php_pqconn_object_read_encoding(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; RETVAL_STRING(pg_encoding_to_char(PQclientEncoding(obj->intern->conn)), 1); } static void php_pqconn_object_write_encoding(zval *object, void *o, zval *value TSRMLS_DC) { php_pqconn_object_t *obj = o; zval *zenc = value; if (Z_TYPE_P(value) != IS_STRING) { if (Z_REFCOUNT_P(value) > 1) { zval *tmp; MAKE_STD_ZVAL(tmp); ZVAL_ZVAL(tmp, zenc, 1, 0); convert_to_string(tmp); zenc = tmp; } else { convert_to_string_ex(&zenc); } } if (0 > PQsetClientEncoding(obj->intern->conn, Z_STRVAL_P(zenc))) { php_error(E_NOTICE, "Unrecognized encoding '%s'", Z_STRVAL_P(zenc)); } if (zenc != value) { zval_ptr_dtor(&zenc); } } static void php_pqconn_object_read_unbuffered(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; RETVAL_BOOL(obj->intern->unbuffered); } static void php_pqconn_object_write_unbuffered(zval *object, void *o, zval *value TSRMLS_DC) { php_pqconn_object_t *obj = o; obj->intern->unbuffered = z_is_true(value); } static void php_pqconn_object_read_db(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; char *db = PQdb(obj->intern->conn); if (db) { RETVAL_STRING(db, 1); } else { RETVAL_EMPTY_STRING(); } } static void php_pqconn_object_read_user(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; char *user = PQuser(obj->intern->conn); if (user) { RETVAL_STRING(user, 1); } else { RETVAL_EMPTY_STRING(); } } static void php_pqconn_object_read_pass(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; char *pass = PQpass(obj->intern->conn); if (pass) { RETVAL_STRING(pass, 1); } else { RETVAL_EMPTY_STRING(); } } static void php_pqconn_object_read_host(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; char *host = PQhost(obj->intern->conn); if (host) { RETVAL_STRING(host, 1); } else { RETVAL_EMPTY_STRING(); } } static void php_pqconn_object_read_port(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; char *port = PQport(obj->intern->conn); if (port) { RETVAL_STRING(port, 1); } else { RETVAL_EMPTY_STRING(); } } #if HAVE_PQCONNINFO static void php_pqconn_object_read_params(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; PQconninfoOption *ptr, *params = PQconninfo(obj->intern->conn); array_init(return_value); if (params) { for (ptr = params; ptr->keyword; ++ptr) { if (ptr->val) { add_assoc_string(return_value, ptr->keyword, ptr->val, 1); } else { add_assoc_null(return_value, ptr->keyword); } } PQconninfoFree(params); } } #endif static void php_pqconn_object_read_options(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; char *options = PQoptions(obj->intern->conn); if (options) { RETVAL_STRING(options, 1); } else { RETVAL_EMPTY_STRING(); } } static int apply_read_event_handler_ex(void *p, void *arg TSRMLS_DC) { HashTable *rv = arg; zval *zcb = php_pq_callback_to_zval(p); zend_hash_next_index_insert(rv, &zcb, sizeof(zval *), NULL); return ZEND_HASH_APPLY_KEEP; } static int apply_read_event_handlers(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) { HashTable *evhs = p, *rv = va_arg(argv, HashTable *); zval *entry, **entry_ptr; MAKE_STD_ZVAL(entry); array_init_size(entry, zend_hash_num_elements(evhs)); if (key->nKeyLength) { zend_hash_add(rv, key->arKey, key->nKeyLength, &entry, sizeof(zval *), (void *) &entry_ptr); } else { zend_hash_index_update(rv, key->h, &entry, sizeof(zval *), (void *) &entry_ptr); } zend_hash_apply_with_argument(evhs, apply_read_event_handler_ex, Z_ARRVAL_PP(entry_ptr) TSRMLS_CC); return ZEND_HASH_APPLY_KEEP; } static void php_pqconn_object_read_event_handlers(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; array_init(return_value); zend_hash_apply_with_arguments(&obj->intern->eventhandlers TSRMLS_CC, apply_read_event_handlers, 1, Z_ARRVAL_P(return_value) TSRMLS_CC); } static void php_pqconn_object_read_def_fetch_type(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; RETVAL_LONG(obj->intern->default_fetch_type); } static void php_pqconn_object_write_def_fetch_type(zval *object, void *o, zval *value TSRMLS_DC) { php_pqconn_object_t *obj = o; zval *zft = value; if (Z_TYPE_P(zft) != IS_LONG) { if (Z_REFCOUNT_P(zft) > 1) { zval *tmp; MAKE_STD_ZVAL(tmp); ZVAL_ZVAL(tmp, zft, 1, 0); convert_to_long(tmp); zft = tmp; } else { convert_to_long_ex(&zft); } } obj->intern->default_fetch_type = Z_LVAL_P(zft) & 0x3; /* two bits only */ if (zft != value) { zval_ptr_dtor(&zft); } } static void php_pqconn_object_read_def_txn_isolation(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; RETVAL_LONG(obj->intern->default_txn_isolation); } static void php_pqconn_object_write_def_txn_isolation(zval *object, void *o, zval *value TSRMLS_DC) { php_pqconn_object_t *obj = o; zval *zti = value; if (Z_TYPE_P(zti) != IS_LONG) { if (Z_REFCOUNT_P(zti) > 1) { zval *tmp; MAKE_STD_ZVAL(tmp); ZVAL_ZVAL(tmp, zti, 1, 0); convert_to_long(tmp); zti = tmp; } else { convert_to_long_ex(&zti); } } obj->intern->default_txn_isolation = Z_LVAL_P(zti) & 0x3; /* two bits only */ if (zti != value) { zval_ptr_dtor(&zti); } } static void php_pqconn_object_read_def_txn_readonly(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; RETVAL_BOOL(obj->intern->default_txn_readonly); } static void php_pqconn_object_write_def_txn_readonly(zval *object, void *o, zval *value TSRMLS_DC) { php_pqconn_object_t *obj = o; obj->intern->default_txn_readonly = zend_is_true(value); } static void php_pqconn_object_read_def_txn_deferrable(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; RETVAL_BOOL(obj->intern->default_txn_deferrable); } static void php_pqconn_object_write_def_txn_deferrable(zval *object, void *o, zval *value TSRMLS_DC) { php_pqconn_object_t *obj = o; obj->intern->default_txn_deferrable = zend_is_true(value); } static void php_pqconn_object_read_def_auto_conv(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; RETVAL_LONG(obj->intern->default_auto_convert); } static void php_pqconn_object_write_def_auto_conv(zval*object, void *o, zval *value TSRMLS_DC) { php_pqconn_object_t *obj = o; zval *zac = value; if (Z_TYPE_P(zac) != IS_LONG) { if (Z_REFCOUNT_P(zac) > 1) { zval *tmp; MAKE_STD_ZVAL(tmp); ZVAL_ZVAL(tmp, zac, 1, 0); convert_to_long(tmp); zac = tmp; } else { convert_to_long_ex(&zac); } } obj->intern->default_auto_convert = Z_LVAL_P(zac) & PHP_PQRES_CONV_ALL; if (zac != value) { zval_ptr_dtor(&zac); } } static STATUS php_pqconn_update_socket(zval *this_ptr, php_pqconn_object_t *obj TSRMLS_DC) { zval *zsocket, zmember; php_stream *stream; STATUS retval; int socket; if (!obj) { obj = zend_object_store_get_object(getThis() TSRMLS_CC); } INIT_PZVAL(&zmember); ZVAL_STRINGL(&zmember, "socket", sizeof("socket")-1, 0); MAKE_STD_ZVAL(zsocket); if ((CONNECTION_BAD != PQstatus(obj->intern->conn)) && (-1 < (socket = PQsocket(obj->intern->conn))) && (stream = php_stream_fopen_from_fd(socket, "r+b", NULL))) { stream->flags |= PHP_STREAM_FLAG_NO_CLOSE; php_stream_to_zval(stream, zsocket); retval = SUCCESS; } else { ZVAL_NULL(zsocket); retval = FAILURE; } zend_get_std_object_handlers()->write_property(getThis(), &zmember, zsocket, NULL TSRMLS_CC); zval_ptr_dtor(&zsocket); return retval; } static void *php_pqconn_resource_factory_ctor(void *data, void *init_arg TSRMLS_DC) { php_pqconn_resource_factory_data_t *o = init_arg; PGconn *conn = NULL;; if (o->flags & PHP_PQCONN_ASYNC) { conn = PQconnectStart(o->dsn); } else { conn = PQconnectdb(o->dsn); } if (conn) { PQregisterEventProc(conn, php_pqconn_event, "ext-pq", NULL); } return conn; } static void php_pqconn_resource_factory_dtor(void *opaque, void *handle TSRMLS_DC) { php_pqconn_event_data_t *evdata = PQinstanceData(handle, php_pqconn_event); /* we don't care for anything, except free'ing evdata */ if (evdata) { PQsetInstanceData(handle, php_pqconn_event, NULL); memset(evdata, 0, sizeof(*evdata)); efree(evdata); } PQfinish(handle); } static php_resource_factory_ops_t php_pqconn_resource_factory_ops = { php_pqconn_resource_factory_ctor, NULL, php_pqconn_resource_factory_dtor }; php_resource_factory_ops_t *php_pqconn_get_resource_factory_ops(void) { return &php_pqconn_resource_factory_ops; } static void php_pqconn_wakeup(php_persistent_handle_factory_t *f, void **handle TSRMLS_DC) { PGresult *res = PQexec(*handle, ""); PHP_PQclear(res); if (CONNECTION_OK != PQstatus(*handle)) { PQreset(*handle); } } static inline PGresult *unlisten(PGconn *conn, const char *channel_str, size_t channel_len TSRMLS_DC) { char *quoted_channel = PQescapeIdentifier(conn, channel_str, channel_len); PGresult *res = NULL; if (quoted_channel) { smart_str cmd = {0}; smart_str_appends(&cmd, "UNLISTEN "); smart_str_appends(&cmd, quoted_channel); smart_str_0(&cmd); res = PQexec(conn, cmd.c); smart_str_free(&cmd); PQfreemem(quoted_channel); } return res; } static int apply_unlisten(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) { php_pqconn_object_t *obj = va_arg(argv, php_pqconn_object_t *); PGresult *res = unlisten(obj->intern->conn, key->arKey, key->nKeyLength - 1 TSRMLS_CC); if (res) { PHP_PQclear(res); } return ZEND_HASH_APPLY_REMOVE; } static void php_pqconn_retire(php_persistent_handle_factory_t *f, void **handle TSRMLS_DC) { php_pqconn_event_data_t *evdata = PQinstanceData(*handle, php_pqconn_event); PGcancel *cancel; PGresult *res; /* go away */ PQsetInstanceData(*handle, php_pqconn_event, NULL); /* ignore notices */ PQsetNoticeReceiver(*handle, php_pqconn_notice_ignore, NULL); /* cancel async queries */ if (PQisBusy(*handle) && (cancel = PQgetCancel(*handle))) { char err[256] = {0}; PQcancel(cancel, err, sizeof(err)); PQfreeCancel(cancel); } /* clean up async results */ while ((res = PQgetResult(*handle))) { PHP_PQclear(res); } /* clean up transaction & session */ switch (PQtransactionStatus(*handle)) { case PQTRANS_IDLE: res = PQexec(*handle, "RESET ALL"); break; default: res = PQexec(*handle, "ROLLBACK; RESET ALL"); break; } if (res) { PHP_PQclear(res); } if (evdata) { /* clean up notify listeners */ zend_hash_apply_with_arguments(&evdata->obj->intern->listeners TSRMLS_CC, apply_unlisten, 1, evdata->obj); /* release instance data */ efree(evdata); } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_construct, 0, 0, 1) ZEND_ARG_INFO(0, dsn) ZEND_ARG_INFO(0, flags) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, __construct) { zend_error_handling zeh; char *dsn_str = ""; int dsn_len = 0; long flags = 0; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sl", &dsn_str, &dsn_len, &flags); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (obj->intern) { throw_exce(EX_BAD_METHODCALL TSRMLS_CC, "pq\\Connection already initialized"); } else { php_pqconn_event_data_t *evdata = php_pqconn_event_data_init(obj TSRMLS_CC); php_pqconn_resource_factory_data_t rfdata = {dsn_str, flags}; obj->intern = ecalloc(1, sizeof(*obj->intern)); obj->intern->default_auto_convert = PHP_PQRES_CONV_ALL; zend_hash_init(&obj->intern->listeners, 0, NULL, (dtor_func_t) zend_hash_destroy, 0); zend_hash_init(&obj->intern->converters, 0, NULL, ZVAL_PTR_DTOR, 0); zend_hash_init(&obj->intern->eventhandlers, 0, NULL, (dtor_func_t) zend_hash_destroy, 0); if (flags & PHP_PQCONN_PERSISTENT) { php_persistent_handle_factory_t *phf = php_persistent_handle_concede(NULL, ZEND_STRL("pq\\Connection"), dsn_str, dsn_len, php_pqconn_wakeup, php_pqconn_retire TSRMLS_CC); php_resource_factory_init(&obj->intern->factory, php_persistent_handle_get_resource_factory_ops(), phf, (void (*)(void*)) php_persistent_handle_abandon); } else { php_resource_factory_init(&obj->intern->factory, &php_pqconn_resource_factory_ops, NULL, NULL); } if (flags & PHP_PQCONN_ASYNC) { obj->intern->poller = (int (*)(PGconn*)) PQconnectPoll; } obj->intern->conn = php_resource_factory_handle_ctor(&obj->intern->factory, &rfdata TSRMLS_CC); PQsetInstanceData(obj->intern->conn, php_pqconn_event, evdata); PQsetNoticeReceiver(obj->intern->conn, php_pqconn_notice_recv, evdata); if (SUCCESS != php_pqconn_update_socket(getThis(), obj TSRMLS_CC)) { throw_exce(EX_CONNECTION_FAILED TSRMLS_CC, "Connection failed (%s)", PHP_PQerrorMessage(obj->intern->conn)); } } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_reset, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, reset) { zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters_none(); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { PQreset(obj->intern->conn); if (CONNECTION_OK != PQstatus(obj->intern->conn)) { throw_exce(EX_CONNECTION_FAILED TSRMLS_CC, "Connection reset failed: (%s)", PHP_PQerrorMessage(obj->intern->conn)); } php_pqconn_notify_listeners(obj TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_reset_async, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, resetAsync) { zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters_none(); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { if (!PQresetStart(obj->intern->conn)) { throw_exce(EX_IO TSRMLS_CC, "Failed to start connection reset (%s)", PHP_PQerrorMessage(obj->intern->conn)); } else { obj->intern->poller = (int (*)(PGconn*)) PQresetPoll; } php_pqconn_notify_listeners(obj TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_unlisten, 0, 0, 1) ZEND_ARG_INFO(0, channel) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, unlisten) { zend_error_handling zeh; char *channel_str; int channel_len; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &channel_str, &channel_len); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else if (SUCCESS == zend_hash_del(&obj->intern->listeners, channel_str, channel_len + 1)) { PGresult *res = unlisten(obj->intern->conn, channel_str, channel_len TSRMLS_CC); if (res) { php_pqres_success(res TSRMLS_CC); PHP_PQclear(res); } } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_unlisten_async, 0, 0, 1) ZEND_ARG_INFO(0, channel) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, unlistenAsync) { zend_error_handling zeh; char *channel_str; int channel_len; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &channel_str, &channel_len); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { char *quoted_channel = PQescapeIdentifier(obj->intern->conn, channel_str, channel_len); if (!quoted_channel) { throw_exce(EX_ESCAPE TSRMLS_CC, "Failed to escape channel identifier (%s)", PHP_PQerrorMessage(obj->intern->conn)); } else { smart_str cmd = {0}; smart_str_appends(&cmd, "UNLISTEN "); smart_str_appends(&cmd, quoted_channel); smart_str_0(&cmd); if (!PQsendQuery(obj->intern->conn, cmd.c)) { throw_exce(EX_IO TSRMLS_CC, "Failed to uninstall listener (%s)", PHP_PQerrorMessage(obj->intern->conn)); } else { obj->intern->poller = PQconsumeInput; zend_hash_del(&obj->intern->listeners, channel_str, channel_len + 1); } smart_str_free(&cmd); PQfreemem(quoted_channel); php_pqconn_notify_listeners(obj TSRMLS_CC); } } } } static void php_pqconn_add_listener(php_pqconn_object_t *obj, const char *channel_str, size_t channel_len, php_pq_callback_t *listener TSRMLS_DC) { HashTable ht, *existing_listeners; php_pq_callback_addref(listener); if (SUCCESS == zend_hash_find(&obj->intern->listeners, channel_str, channel_len + 1, (void *) &existing_listeners)) { zend_hash_next_index_insert(existing_listeners, (void *) listener, sizeof(*listener), NULL); } else { zend_hash_init(&ht, 1, NULL, (dtor_func_t) php_pq_callback_dtor, 0); zend_hash_next_index_insert(&ht, (void *) listener, sizeof(*listener), NULL); zend_hash_add(&obj->intern->listeners, channel_str, channel_len + 1, (void *) &ht, sizeof(HashTable), NULL); } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_listen, 0, 0, 2) ZEND_ARG_INFO(0, channel) ZEND_ARG_INFO(0, callable) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, listen) { zend_error_handling zeh; char *channel_str = NULL; int channel_len = 0; php_pq_callback_t listener = {{0}}; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sf", &channel_str, &channel_len, &listener.fci, &listener.fcc); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { char *quoted_channel = PQescapeIdentifier(obj->intern->conn, channel_str, channel_len); if (!quoted_channel) { throw_exce(EX_ESCAPE TSRMLS_CC, "Failed to escape channel identifier (%s)", PHP_PQerrorMessage(obj->intern->conn)); } else { PGresult *res; smart_str cmd = {0}; smart_str_appends(&cmd, "LISTEN "); smart_str_appends(&cmd, quoted_channel); smart_str_0(&cmd); res = PQexec(obj->intern->conn, cmd.c); smart_str_free(&cmd); PQfreemem(quoted_channel); if (!res) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to install listener (%s)", PHP_PQerrorMessage(obj->intern->conn)); } else { if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { obj->intern->poller = PQconsumeInput; php_pqconn_add_listener(obj, channel_str, channel_len, &listener TSRMLS_CC); } PHP_PQclear(res); } php_pqconn_notify_listeners(obj TSRMLS_CC); } } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_listen_async, 0, 0, 0) ZEND_ARG_INFO(0, channel) ZEND_ARG_INFO(0, callable) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, listenAsync) { zend_error_handling zeh; char *channel_str = NULL; int channel_len = 0; php_pq_callback_t listener = {{0}}; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sf", &channel_str, &channel_len, &listener.fci, &listener.fcc); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { char *quoted_channel = PQescapeIdentifier(obj->intern->conn, channel_str, channel_len); if (!quoted_channel) { throw_exce(EX_ESCAPE TSRMLS_CC, "Failed to escape channel identifier (%s)", PHP_PQerrorMessage(obj->intern->conn)); } else { smart_str cmd = {0}; smart_str_appends(&cmd, "LISTEN "); smart_str_appends(&cmd, quoted_channel); smart_str_0(&cmd); if (!PQsendQuery(obj->intern->conn, cmd.c)) { throw_exce(EX_IO TSRMLS_CC, "Failed to install listener (%s)", PHP_PQerrorMessage(obj->intern->conn)); } else { obj->intern->poller = PQconsumeInput; php_pqconn_add_listener(obj, channel_str, channel_len, &listener TSRMLS_CC); } smart_str_free(&cmd); PQfreemem(quoted_channel); php_pqconn_notify_listeners(obj TSRMLS_CC); } } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_notify, 0, 0, 2) ZEND_ARG_INFO(0, channel) ZEND_ARG_INFO(0, message) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, notify) { zend_error_handling zeh; char *channel_str, *message_str; int channel_len, message_len; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &channel_str, &channel_len, &message_str, &message_len); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { PGresult *res; char *params[2] = {channel_str, message_str}; res = PQexecParams(obj->intern->conn, "select pg_notify($1, $2)", 2, NULL, (const char *const*) params, NULL, NULL, 0); if (!res) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to notify listeners (%s)", PHP_PQerrorMessage(obj->intern->conn)); } else { php_pqres_success(res TSRMLS_CC); PHP_PQclear(res); } php_pqconn_notify_listeners(obj TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_notify_async, 0, 0, 2) ZEND_ARG_INFO(0, channel) ZEND_ARG_INFO(0, message) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, notifyAsync) { zend_error_handling zeh; char *channel_str, *message_str; int channel_len, message_len; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &channel_str, &channel_len, &message_str, &message_len); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { char *params[2] = {channel_str, message_str}; if (!PQsendQueryParams(obj->intern->conn, "select pg_notify($1, $2)", 2, NULL, (const char *const*) params, NULL, NULL, 0)) { throw_exce(EX_IO TSRMLS_CC, "Failed to notify listeners (%s)", PHP_PQerrorMessage(obj->intern->conn)); } else { obj->intern->poller = PQconsumeInput; } php_pqconn_notify_listeners(obj TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_poll, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, poll) { zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters_none(); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else if (!obj->intern->poller) { throw_exce(EX_RUNTIME TSRMLS_CC, "No asynchronous operation active"); } else { if (obj->intern->poller == PQconsumeInput) { RETVAL_LONG(obj->intern->poller(obj->intern->conn) * PGRES_POLLING_OK); } else { RETVAL_LONG(obj->intern->poller(obj->intern->conn)); } php_pqconn_notify_listeners(obj TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_exec, 0, 0, 1) ZEND_ARG_INFO(0, query) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, exec) { zend_error_handling zeh; char *query_str; int query_len; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &query_str, &query_len); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { PGresult *res = PQexec(obj->intern->conn, query_str); if (!res) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to execute query (%s)", PHP_PQerrorMessage(obj->intern->conn)); } else if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { php_pq_object_to_zval_no_addref(PQresultInstanceData(res, php_pqconn_event), &return_value TSRMLS_CC); } else { PHP_PQclear(res); } php_pqconn_notify_listeners(obj TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_get_result, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, getResult) { zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters_none(); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { PGresult *res = PQgetResult(obj->intern->conn); if (!res) { RETVAL_NULL(); } else { php_pq_object_to_zval_no_addref(PQresultInstanceData(res, php_pqconn_event), &return_value TSRMLS_CC); } php_pqconn_notify_listeners(obj TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_exec_async, 0, 0, 1) ZEND_ARG_INFO(0, query) ZEND_ARG_INFO(0, callable) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, execAsync) { zend_error_handling zeh; php_pq_callback_t resolver = {{0}}; char *query_str; int query_len; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|f", &query_str, &query_len, &resolver.fci, &resolver.fcc); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else if (!PQsendQuery(obj->intern->conn, query_str)) { throw_exce(EX_IO TSRMLS_CC, "Failed to execute query (%s)", PHP_PQerrorMessage(obj->intern->conn)); #if HAVE_PQSETSINGLEROWMODE } else if (obj->intern->unbuffered && !PQsetSingleRowMode(obj->intern->conn)) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn)); #endif } else { php_pq_callback_recurse(&obj->intern->onevent, &resolver TSRMLS_CC); obj->intern->poller = PQconsumeInput; php_pqconn_notify_listeners(obj TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_exec_params, 0, 0, 2) ZEND_ARG_INFO(0, query) ZEND_ARG_ARRAY_INFO(0, params, 0) ZEND_ARG_ARRAY_INFO(0, types, 1) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, execParams) { zend_error_handling zeh; char *query_str; int query_len; zval *zparams; zval *ztypes = NULL; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|a/!", &query_str, &query_len, &zparams, &ztypes); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { PGresult *res; php_pq_params_t *params; params = php_pq_params_init(&obj->intern->converters, ztypes ? Z_ARRVAL_P(ztypes) : NULL, Z_ARRVAL_P(zparams) TSRMLS_CC); res = PQexecParams(obj->intern->conn, query_str, params->param.count, params->type.oids, (const char *const*) params->param.strings, NULL, NULL, 0); php_pq_params_free(¶ms); if (!res) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to execute query (%s)", PHP_PQerrorMessage(obj->intern->conn)); } else { if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { php_pq_object_to_zval_no_addref(PQresultInstanceData(res, php_pqconn_event), &return_value TSRMLS_CC); } else { PHP_PQclear(res); } php_pqconn_notify_listeners(obj TSRMLS_CC); } } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_exec_params_async, 0, 0, 2) ZEND_ARG_INFO(0, query) ZEND_ARG_ARRAY_INFO(0, params, 0) ZEND_ARG_ARRAY_INFO(0, types, 1) ZEND_ARG_INFO(0, callable) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, execParamsAsync) { zend_error_handling zeh; php_pq_callback_t resolver = {{0}}; char *query_str; int query_len; zval *zparams; zval *ztypes = NULL; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|a/!f", &query_str, &query_len, &zparams, &ztypes, &resolver.fci, &resolver.fcc); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { int rc; php_pq_params_t *params; params = php_pq_params_init(&obj->intern->converters, ztypes ? Z_ARRVAL_P(ztypes) : NULL, Z_ARRVAL_P(zparams) TSRMLS_CC); rc = PQsendQueryParams(obj->intern->conn, query_str, params->param.count, params->type.oids, (const char *const*) params->param.strings, NULL, NULL, 0); php_pq_params_free(¶ms); if (!rc) { throw_exce(EX_IO TSRMLS_CC, "Failed to execute query (%s)", PHP_PQerrorMessage(obj->intern->conn)); #if HAVE_PQSETSINGLEROWMODE } else if (obj->intern->unbuffered && !PQsetSingleRowMode(obj->intern->conn)) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn)); #endif } else { php_pq_callback_recurse(&obj->intern->onevent, &resolver TSRMLS_CC); obj->intern->poller = PQconsumeInput; php_pqconn_notify_listeners(obj TSRMLS_CC); } } } zend_restore_error_handling(&zeh TSRMLS_CC); } STATUS php_pqconn_prepare(zval *object, php_pqconn_object_t *obj, const char *name, const char *query, php_pq_params_t *params TSRMLS_DC) { PGresult *res; STATUS rv; if (!obj) { obj = zend_object_store_get_object(object TSRMLS_CC); } res = PQprepare(obj->intern->conn, name, query, params->type.count, params->type.oids); if (!res) { rv = FAILURE; throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to prepare statement (%s)", PHP_PQerrorMessage(obj->intern->conn)); } else { rv = php_pqres_success(res TSRMLS_CC); PHP_PQclear(res); php_pqconn_notify_listeners(obj TSRMLS_CC); } return rv; } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_prepare, 0, 0, 2) ZEND_ARG_INFO(0, name) ZEND_ARG_INFO(0, query) ZEND_ARG_ARRAY_INFO(0, types, 1) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, prepare) { zend_error_handling zeh; zval *ztypes = NULL; char *name_str, *query_str; int name_len, *query_len; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!", &name_str, &name_len, &query_str, &query_len, &ztypes); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { php_pq_params_t *params = php_pq_params_init(&obj->intern->converters, ztypes ? Z_ARRVAL_P(ztypes) : NULL, NULL TSRMLS_CC); if (SUCCESS != php_pqconn_prepare(getThis(), obj, name_str, query_str, params TSRMLS_CC)) { php_pq_params_free(¶ms); } else { php_pqstm_t *stm = php_pqstm_init(obj, name_str, query_str, params TSRMLS_CC); return_value->type = IS_OBJECT; return_value->value.obj = php_pqstm_create_object_ex(php_pqstm_class_entry, stm, NULL TSRMLS_CC); } } } } STATUS php_pqconn_prepare_async(zval *object, php_pqconn_object_t *obj, const char *name, const char *query, php_pq_params_t *params TSRMLS_DC) { STATUS rv; if (!obj) { obj = zend_object_store_get_object(object TSRMLS_CC); } if (!PQsendPrepare(obj->intern->conn, name, query, params->type.count, params->type.oids)) { rv = FAILURE; throw_exce(EX_IO TSRMLS_CC, "Failed to prepare statement (%s)", PHP_PQerrorMessage(obj->intern->conn)); } else { rv = SUCCESS; obj->intern->poller = PQconsumeInput; php_pqconn_notify_listeners(obj TSRMLS_CC); } return rv; } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_prepare_async, 0, 0, 2) ZEND_ARG_INFO(0, name) ZEND_ARG_INFO(0, query) ZEND_ARG_ARRAY_INFO(0, types, 1) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, prepareAsync) { zend_error_handling zeh; zval *ztypes = NULL; char *name_str, *query_str; int name_len, *query_len; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!", &name_str, &name_len, &query_str, &query_len, &ztypes); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { php_pq_params_t *params = php_pq_params_init(&obj->intern->converters, ztypes ? Z_ARRVAL_P(ztypes) : NULL, NULL TSRMLS_CC); if (SUCCESS != php_pqconn_prepare_async(getThis(), obj, name_str, query_str, params TSRMLS_CC)) { php_pq_params_free(¶ms); } else { php_pqstm_t *stm = php_pqstm_init(obj, name_str, query_str, params TSRMLS_CC); return_value->type = IS_OBJECT; return_value->value.obj = php_pqstm_create_object_ex(php_pqstm_class_entry, stm, NULL TSRMLS_CC); } } } } STATUS php_pqconn_declare(zval *object, php_pqconn_object_t *obj, const char *decl TSRMLS_DC) { PGresult *res; STATUS rv; if (!obj) { obj = zend_object_store_get_object(object TSRMLS_CC); } res = PQexec(obj->intern->conn, decl); if (!res) { rv = FAILURE; throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to declare cursor (%s)", PHP_PQerrorMessage(obj->intern->conn)); } else { rv = php_pqres_success(res TSRMLS_CC); PHP_PQclear(res); php_pqconn_notify_listeners(obj TSRMLS_CC); } return rv; } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_declare, 0, 0, 3) ZEND_ARG_INFO(0, name) ZEND_ARG_INFO(0, flags) ZEND_ARG_INFO(0, query) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, declare) { zend_error_handling zeh; char *name_str, *query_str; int name_len, query_len; long flags; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sls", &name_str, &name_len, &flags, &query_str, &query_len); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { char *decl = php_pqcur_declare_str(name_str, name_len, flags, query_str, query_len); if (SUCCESS != php_pqconn_declare(getThis(), obj, decl TSRMLS_CC)) { efree(decl); } else { php_pqcur_t *cur = ecalloc(1, sizeof(*cur)); php_pq_object_addref(obj TSRMLS_CC); cur->conn = obj; cur->open = 1; cur->name = estrdup(name_str); cur->decl = decl; return_value->type = IS_OBJECT; return_value->value.obj = php_pqcur_create_object_ex(php_pqcur_class_entry, cur, NULL TSRMLS_CC); } } } } STATUS php_pqconn_declare_async(zval *object, php_pqconn_object_t *obj, const char *decl TSRMLS_DC) { STATUS rv; if (!obj) { obj = zend_object_store_get_object(object TSRMLS_CC); } if (!PQsendQuery(obj->intern->conn, decl)) { rv = FAILURE; throw_exce(EX_IO TSRMLS_CC, "Failed to declare cursor (%s)", PHP_PQerrorMessage(obj->intern->conn)); } else { rv = SUCCESS; obj->intern->poller = PQconsumeInput; php_pqconn_notify_listeners(obj TSRMLS_CC); } return rv; } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_declare_async, 0, 0, 2) ZEND_ARG_INFO(0, name) ZEND_ARG_INFO(0, flags) ZEND_ARG_INFO(0, query) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, declareAsync) { zend_error_handling zeh; char *name_str, *query_str; int name_len, query_len; long flags; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sls", &name_str, &name_len, &flags, &query_str, &query_len); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { char *decl = php_pqcur_declare_str(name_str, name_len, flags, query_str, query_len); if (SUCCESS != php_pqconn_declare_async(getThis(), obj, decl TSRMLS_CC)) { efree(decl); } else { php_pqcur_t *cur = ecalloc(1, sizeof(*cur)); php_pq_object_addref(obj TSRMLS_CC); cur->conn = obj; cur->open = 1; cur->name = estrdup(name_str); cur->decl = decl; return_value->type = IS_OBJECT; return_value->value.obj = php_pqcur_create_object_ex(php_pqcur_class_entry, cur, NULL TSRMLS_CC); } } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_quote, 0, 0, 1) ZEND_ARG_INFO(0, string) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, quote) { char *str; int len; if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len)) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { char *quoted = PQescapeLiteral(obj->intern->conn, str, len); if (!quoted) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to quote string (%s)", PHP_PQerrorMessage(obj->intern->conn)); RETVAL_FALSE; } else { RETVAL_STRING(quoted, 1); PQfreemem(quoted); } } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_quote_name, 0, 0, 1) ZEND_ARG_INFO(0, type) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, quoteName) { char *str; int len; if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len)) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { char *quoted = PQescapeIdentifier(obj->intern->conn, str, len); if (!quoted) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to quote name (%s)", PHP_PQerrorMessage(obj->intern->conn)); RETVAL_FALSE; } else { RETVAL_STRING(quoted, 1); PQfreemem(quoted); } } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_escape_bytea, 0, 0, 1) ZEND_ARG_INFO(0, bytea) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, escapeBytea) { char *str; int len; if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len)) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { size_t escaped_len; char *escaped_str = (char *) PQescapeByteaConn(obj->intern->conn, (unsigned char *) str, len, &escaped_len); if (!escaped_str) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to escape bytea (%s)", PHP_PQerrorMessage(obj->intern->conn)); RETVAL_FALSE; } else { RETVAL_STRINGL(escaped_str, escaped_len - 1, 1); PQfreemem(escaped_str); } } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_unescape_bytea, 0, 0, 1) ZEND_ARG_INFO(0, bytea) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, unescapeBytea) { char *str; int len; if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len)) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { size_t unescaped_len; char *unescaped_str = (char *) PQunescapeBytea((unsigned char *)str, &unescaped_len); if (!unescaped_str) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to unescape bytea (%s)", PHP_PQerrorMessage(obj->intern->conn)); RETVAL_FALSE; } else { RETVAL_STRINGL(unescaped_str, unescaped_len, 1); PQfreemem(unescaped_str); } } } } STATUS php_pqconn_start_transaction(zval *zconn, php_pqconn_object_t *conn_obj, long isolation, zend_bool readonly, zend_bool deferrable TSRMLS_DC) { STATUS rv = FAILURE; if (!conn_obj) { conn_obj = zend_object_store_get_object(zconn TSRMLS_CC); } if (!conn_obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { PGresult *res; smart_str cmd = {0}; const char *il = php_pq_isolation_level(&isolation); smart_str_appends(&cmd, "START TRANSACTION ISOLATION LEVEL "); smart_str_appends(&cmd, il); smart_str_appends(&cmd, ", READ "); smart_str_appends(&cmd, readonly ? "ONLY" : "WRITE"); smart_str_appends(&cmd, ","); smart_str_appends(&cmd, deferrable ? "" : " NOT"); smart_str_appends(&cmd, " DEFERRABLE"); smart_str_0(&cmd); res = PQexec(conn_obj->intern->conn, cmd.c); if (!res) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to start transaction (%s)", PHP_PQerrorMessage(conn_obj->intern->conn)); } else { rv = php_pqres_success(res TSRMLS_CC); PHP_PQclear(res); php_pqconn_notify_listeners(conn_obj TSRMLS_CC); } smart_str_free(&cmd); } return rv; } STATUS php_pqconn_start_transaction_async(zval *zconn, php_pqconn_object_t *conn_obj, long isolation, zend_bool readonly, zend_bool deferrable TSRMLS_DC) { STATUS rv = FAILURE; if (!conn_obj) { conn_obj = zend_object_store_get_object(zconn TSRMLS_CC); } if (!conn_obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { smart_str cmd = {0}; const char *il = php_pq_isolation_level(&isolation); smart_str_appends(&cmd, "START TRANSACTION ISOLATION LEVEL "); smart_str_appends(&cmd, il); smart_str_appends(&cmd, ", READ "); smart_str_appends(&cmd, readonly ? "ONLY" : "WRITE"); smart_str_appends(&cmd, ","); smart_str_appends(&cmd, deferrable ? "" : "NOT "); smart_str_appends(&cmd, " DEFERRABLE"); smart_str_0(&cmd); if (!PQsendQuery(conn_obj->intern->conn, cmd.c)) { throw_exce(EX_IO TSRMLS_CC, "Failed to start transaction (%s)", PHP_PQerrorMessage(conn_obj->intern->conn)); } else { rv = SUCCESS; conn_obj->intern->poller = PQconsumeInput; php_pqconn_notify_listeners(conn_obj TSRMLS_CC); } smart_str_free(&cmd); } return rv; } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_start_transaction, 0, 0, 0) ZEND_ARG_INFO(0, isolation) ZEND_ARG_INFO(0, readonly) ZEND_ARG_INFO(0, deferrable) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, startTransaction) { zend_error_handling zeh; php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); long isolation = obj->intern ? obj->intern->default_txn_isolation : PHP_PQTXN_READ_COMMITTED; zend_bool readonly = obj->intern ? obj->intern->default_txn_readonly : 0; zend_bool deferrable = obj->intern ? obj->intern->default_txn_deferrable : 0; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lbb", &isolation, &readonly, &deferrable); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { rv = php_pqconn_start_transaction(getThis(), obj, isolation, readonly, deferrable TSRMLS_CC); if (SUCCESS == rv) { php_pqtxn_t *txn = ecalloc(1, sizeof(*txn)); php_pq_object_addref(obj TSRMLS_CC); txn->conn = obj; txn->open = 1; txn->isolation = isolation; txn->readonly = readonly; txn->deferrable = deferrable; return_value->type = IS_OBJECT; return_value->value.obj = php_pqtxn_create_object_ex(php_pqtxn_class_entry, txn, NULL TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_start_transaction_async, 0, 0, 0) ZEND_ARG_INFO(0, isolation) ZEND_ARG_INFO(0, readonly) ZEND_ARG_INFO(0, deferrable) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, startTransactionAsync) { zend_error_handling zeh; php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); long isolation = obj->intern ? obj->intern->default_txn_isolation : PHP_PQTXN_READ_COMMITTED; zend_bool readonly = obj->intern ? obj->intern->default_txn_readonly : 0; zend_bool deferrable = obj->intern ? obj->intern->default_txn_deferrable : 0; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lbb", &isolation, &readonly, &deferrable); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { rv = php_pqconn_start_transaction_async(getThis(), obj, isolation, readonly, deferrable TSRMLS_CC); if (SUCCESS == rv) { php_pqtxn_t *txn = ecalloc(1, sizeof(*txn)); php_pq_object_addref(obj TSRMLS_CC); txn->conn = obj; txn->open = 1; txn->isolation = isolation; txn->readonly = readonly; txn->deferrable = deferrable; return_value->type = IS_OBJECT; return_value->value.obj = php_pqtxn_create_object_ex(php_pqtxn_class_entry, txn, NULL TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_trace, 0, 0, 0) ZEND_ARG_INFO(0, stdio_stream) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, trace) { zval *zstream = NULL; if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r!", &zstream)) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { if (!zstream) { PQuntrace(obj->intern->conn); RETVAL_TRUE; } else { FILE *fp; php_stream *stream = NULL; php_stream_from_zval(stream, &zstream); if (SUCCESS != php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void *) &fp, REPORT_ERRORS)) { RETVAL_FALSE; } else { stream->flags |= PHP_STREAM_FLAG_NO_CLOSE; PQtrace(obj->intern->conn, fp); RETVAL_TRUE; } } } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_off, 0, 0, 1) ZEND_ARG_INFO(0, type) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, off) { zend_error_handling zeh; char *type_str; int type_len; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &type_str, &type_len); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { RETURN_BOOL(SUCCESS == zend_hash_del(&obj->intern->eventhandlers, type_str, type_len + 1)); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_on, 0, 0, 2) ZEND_ARG_INFO(0, type) ZEND_ARG_INFO(0, callable) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, on) { zend_error_handling zeh; char *type_str; int type_len; php_pq_callback_t cb = {{0}}; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sf", &type_str, &type_len, &cb.fci, &cb.fcc); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); RETVAL_LONG(php_pqconn_add_eventhandler(obj, type_str, type_len, &cb TSRMLS_CC)); } } } struct apply_set_converter_arg { HashTable *ht; zval **zconv; unsigned add:1; }; static int apply_set_converter(void *p, void *a TSRMLS_DC) { zval *tmp, **zoid = p; struct apply_set_converter_arg *arg = a; tmp = *zoid; Z_ADDREF_P(tmp); convert_to_long_ex(&tmp); if (arg->add) { Z_ADDREF_PP(arg->zconv); zend_hash_index_update(arg->ht, Z_LVAL_P(tmp), arg->zconv, sizeof(zval *), NULL); } else { zend_hash_index_del(arg->ht, Z_LVAL_P(tmp)); } zval_ptr_dtor(&tmp); return ZEND_HASH_APPLY_KEEP; } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_set_converter, 0, 0, 1) ZEND_ARG_OBJ_INFO(0, converter, pq\\Converter, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, setConverter) { STATUS rv; zend_error_handling zeh; zval *zcnv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zcnv, php_pqconv_class_entry); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { zval *tmp, *zoids = NULL; struct apply_set_converter_arg arg = {NULL}; zend_call_method_with_0_params(&zcnv, NULL, NULL, "converttypes", &zoids); tmp = zoids; Z_ADDREF_P(tmp); convert_to_array_ex(&tmp); arg.ht = &obj->intern->converters; arg.zconv = &zcnv; arg.add = 1; zend_hash_apply_with_argument(Z_ARRVAL_P(tmp), apply_set_converter, &arg TSRMLS_CC); zval_ptr_dtor(&tmp); zval_ptr_dtor(&zoids); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_unset_converter, 0, 0, 1) ZEND_ARG_OBJ_INFO(0, converter, pq\\Converter, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, unsetConverter) { STATUS rv; zend_error_handling zeh; zval *zcnv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zcnv, php_pqconv_class_entry); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { zval *tmp, *zoids = NULL; struct apply_set_converter_arg arg = {NULL}; zend_call_method_with_0_params(&zcnv, NULL, NULL, "converttypes", &zoids); tmp = zoids; Z_ADDREF_P(tmp); convert_to_array_ex(&tmp); arg.ht = &obj->intern->converters; arg.zconv = &zcnv; arg.add = 0; zend_hash_apply_with_argument(Z_ARRVAL_P(tmp), apply_set_converter, &arg TSRMLS_CC); zval_ptr_dtor(&tmp); zval_ptr_dtor(&zoids); } } } static zend_function_entry php_pqconn_methods[] = { PHP_ME(pqconn, __construct, ai_pqconn_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) PHP_ME(pqconn, reset, ai_pqconn_reset, ZEND_ACC_PUBLIC) PHP_ME(pqconn, resetAsync, ai_pqconn_reset_async, ZEND_ACC_PUBLIC) PHP_ME(pqconn, poll, ai_pqconn_poll, ZEND_ACC_PUBLIC) PHP_ME(pqconn, exec, ai_pqconn_exec, ZEND_ACC_PUBLIC) PHP_ME(pqconn, execAsync, ai_pqconn_exec_async, ZEND_ACC_PUBLIC) PHP_ME(pqconn, execParams, ai_pqconn_exec_params, ZEND_ACC_PUBLIC) PHP_ME(pqconn, execParamsAsync, ai_pqconn_exec_params_async, ZEND_ACC_PUBLIC) PHP_ME(pqconn, prepare, ai_pqconn_prepare, ZEND_ACC_PUBLIC) PHP_ME(pqconn, prepareAsync, ai_pqconn_prepare_async, ZEND_ACC_PUBLIC) PHP_ME(pqconn, declare, ai_pqconn_declare, ZEND_ACC_PUBLIC) PHP_ME(pqconn, declareAsync, ai_pqconn_declare_async, ZEND_ACC_PUBLIC) PHP_ME(pqconn, unlisten, ai_pqconn_unlisten, ZEND_ACC_PUBLIC) PHP_ME(pqconn, unlistenAsync, ai_pqconn_unlisten_async, ZEND_ACC_PUBLIC) PHP_ME(pqconn, listen, ai_pqconn_listen, ZEND_ACC_PUBLIC) PHP_ME(pqconn, listenAsync, ai_pqconn_listen_async, ZEND_ACC_PUBLIC) PHP_ME(pqconn, notify, ai_pqconn_notify, ZEND_ACC_PUBLIC) PHP_ME(pqconn, notifyAsync, ai_pqconn_notify_async, ZEND_ACC_PUBLIC) PHP_ME(pqconn, getResult, ai_pqconn_get_result, ZEND_ACC_PUBLIC) PHP_ME(pqconn, quote, ai_pqconn_quote, ZEND_ACC_PUBLIC) PHP_ME(pqconn, quoteName, ai_pqconn_quote_name, ZEND_ACC_PUBLIC) PHP_ME(pqconn, escapeBytea, ai_pqconn_escape_bytea, ZEND_ACC_PUBLIC) PHP_ME(pqconn, unescapeBytea, ai_pqconn_unescape_bytea, ZEND_ACC_PUBLIC) PHP_ME(pqconn, startTransaction, ai_pqconn_start_transaction, ZEND_ACC_PUBLIC) PHP_ME(pqconn, startTransactionAsync, ai_pqconn_start_transaction_async, ZEND_ACC_PUBLIC) PHP_ME(pqconn, trace, ai_pqconn_trace, ZEND_ACC_PUBLIC) PHP_ME(pqconn, off, ai_pqconn_off, ZEND_ACC_PUBLIC) PHP_ME(pqconn, on, ai_pqconn_on, ZEND_ACC_PUBLIC) PHP_ME(pqconn, setConverter, ai_pqconn_set_converter, ZEND_ACC_PUBLIC) PHP_ME(pqconn, unsetConverter, ai_pqconn_unset_converter, ZEND_ACC_PUBLIC) {0} }; PHP_MSHUTDOWN_FUNCTION(pqconn) { zend_hash_destroy(&php_pqconn_object_prophandlers); return SUCCESS; } PHP_MINIT_FUNCTION(pqconn) { zend_class_entry ce = {0}; php_pq_object_prophandler_t ph = {0}; INIT_NS_CLASS_ENTRY(ce, "pq", "Connection", php_pqconn_methods); php_pqconn_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC); php_pqconn_class_entry->create_object = php_pqconn_create_object; memcpy(&php_pqconn_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); php_pqconn_object_handlers.read_property = php_pq_object_read_prop; php_pqconn_object_handlers.write_property = php_pq_object_write_prop; php_pqconn_object_handlers.clone_obj = NULL; php_pqconn_object_handlers.get_property_ptr_ptr = NULL; php_pqconn_object_handlers.get_gc = NULL; php_pqconn_object_handlers.get_properties = php_pq_object_properties; php_pqconn_object_handlers.get_debug_info = php_pq_object_debug_info; zend_hash_init(&php_pqconn_object_prophandlers, 20, NULL, NULL, 1); zend_declare_property_long(php_pqconn_class_entry, ZEND_STRL("status"), CONNECTION_BAD, ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqconn_object_read_status; zend_hash_add(&php_pqconn_object_prophandlers, "status", sizeof("status"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_long(php_pqconn_class_entry, ZEND_STRL("transactionStatus"), PQTRANS_UNKNOWN, ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqconn_object_read_transaction_status; zend_hash_add(&php_pqconn_object_prophandlers, "transactionStatus", sizeof("transactionStatus"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("socket"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = NULL; /* forward to std prophandler */ zend_hash_add(&php_pqconn_object_prophandlers, "socket", sizeof("socket"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("errorMessage"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqconn_object_read_error_message; zend_hash_add(&php_pqconn_object_prophandlers, "errorMessage", sizeof("errorMessage"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_bool(php_pqconn_class_entry, ZEND_STRL("busy"), 0, ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqconn_object_read_busy; zend_hash_add(&php_pqconn_object_prophandlers, "busy", sizeof("busy"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("encoding"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqconn_object_read_encoding; ph.write = php_pqconn_object_write_encoding; zend_hash_add(&php_pqconn_object_prophandlers, "encoding", sizeof("encoding"), (void *) &ph, sizeof(ph), NULL); ph.write = NULL; zend_declare_property_bool(php_pqconn_class_entry, ZEND_STRL("unbuffered"), 0, ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqconn_object_read_unbuffered; ph.write = php_pqconn_object_write_unbuffered; zend_hash_add(&php_pqconn_object_prophandlers, "unbuffered", sizeof("unbuffered"), (void *) &ph, sizeof(ph), NULL); ph.write = NULL; zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("db"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqconn_object_read_db; zend_hash_add(&php_pqconn_object_prophandlers, "db", sizeof("db"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("user"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqconn_object_read_user; zend_hash_add(&php_pqconn_object_prophandlers, "user", sizeof("user"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("pass"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqconn_object_read_pass; zend_hash_add(&php_pqconn_object_prophandlers, "pass", sizeof("pass"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("host"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqconn_object_read_host; zend_hash_add(&php_pqconn_object_prophandlers, "host", sizeof("host"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("port"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqconn_object_read_port; zend_hash_add(&php_pqconn_object_prophandlers, "port", sizeof("port"), (void *) &ph, sizeof(ph), NULL); #if HAVE_PQCONNINFO zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("params"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqconn_object_read_params; zend_hash_add(&php_pqconn_object_prophandlers, "params", sizeof("params"), (void *) &ph, sizeof(ph), NULL); #endif zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("options"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqconn_object_read_options; zend_hash_add(&php_pqconn_object_prophandlers, "options", sizeof("options"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("eventHandlers"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqconn_object_read_event_handlers; zend_hash_add(&php_pqconn_object_prophandlers, "eventHandlers", sizeof("eventHandlers"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_long(php_pqconn_class_entry, ZEND_STRL("defaultFetchType"), 0, ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqconn_object_read_def_fetch_type; ph.write = php_pqconn_object_write_def_fetch_type; zend_hash_add(&php_pqconn_object_prophandlers, "defaultFetchType", sizeof("defaultFetchType"), (void *) &ph, sizeof(ph), NULL); ph.write = NULL; zend_declare_property_long(php_pqconn_class_entry, ZEND_STRL("defaultTransactionIsolation"), 0, ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqconn_object_read_def_txn_isolation; ph.write = php_pqconn_object_write_def_txn_isolation; zend_hash_add(&php_pqconn_object_prophandlers, "defaultTransactionIsolation", sizeof("defaultTransactionIsolation"), (void *) &ph, sizeof(ph), NULL); ph.write = NULL; zend_declare_property_bool(php_pqconn_class_entry, ZEND_STRL("defaultTransactionReadonly"), 0, ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqconn_object_read_def_txn_readonly; ph.write = php_pqconn_object_write_def_txn_readonly; zend_hash_add(&php_pqconn_object_prophandlers, "defaultTransactionReadonly", sizeof("defaultTransactionReadonly"), (void *) &ph, sizeof(ph), NULL); ph.write = NULL; zend_declare_property_bool(php_pqconn_class_entry, ZEND_STRL("defaultTransactionDeferrable"), 0, ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqconn_object_read_def_txn_deferrable; ph.write = php_pqconn_object_write_def_txn_deferrable; zend_hash_add(&php_pqconn_object_prophandlers, "defaultTransactionDeferrable", sizeof("defaultTransactionDeferrable"), (void *) &ph, sizeof(ph), NULL); ph.write = NULL; zend_declare_property_long(php_pqconn_class_entry, ZEND_STRL("defaultAutoConvert"), PHP_PQRES_CONV_ALL, ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqconn_object_read_def_auto_conv; ph.write = php_pqconn_object_write_def_auto_conv; zend_hash_add(&php_pqconn_object_prophandlers, "defaultAutoConvert", sizeof("defaultAutoConvert"), (void *) &ph, sizeof(ph), NULL); ph.write = NULL; zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("OK"), CONNECTION_OK TSRMLS_CC); zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("BAD"), CONNECTION_BAD TSRMLS_CC); zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("STARTED"), CONNECTION_STARTED TSRMLS_CC); zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("MADE"), CONNECTION_MADE TSRMLS_CC); zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("AWAITING_RESPONSE"), CONNECTION_AWAITING_RESPONSE TSRMLS_CC); zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("AUTH_OK"), CONNECTION_AUTH_OK TSRMLS_CC); zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("SSL_STARTUP"), CONNECTION_SSL_STARTUP TSRMLS_CC); zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("SETENV"), CONNECTION_SETENV TSRMLS_CC); zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("TRANS_IDLE"), PQTRANS_IDLE TSRMLS_CC); zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("TRANS_ACTIVE"), PQTRANS_ACTIVE TSRMLS_CC); zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("TRANS_INTRANS"), PQTRANS_INTRANS TSRMLS_CC); zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("TRANS_INERROR"), PQTRANS_INERROR TSRMLS_CC); zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("TRANS_UNKNOWN"), PQTRANS_UNKNOWN TSRMLS_CC); zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("POLLING_FAILED"), PGRES_POLLING_FAILED TSRMLS_CC); zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("POLLING_READING"), PGRES_POLLING_READING TSRMLS_CC); zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("POLLING_WRITING"), PGRES_POLLING_WRITING TSRMLS_CC); zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("POLLING_OK"), PGRES_POLLING_OK TSRMLS_CC); zend_declare_class_constant_stringl(php_pqconn_class_entry, ZEND_STRL("EVENT_NOTICE"), ZEND_STRL("notice") TSRMLS_CC); zend_declare_class_constant_stringl(php_pqconn_class_entry, ZEND_STRL("EVENT_RESULT"), ZEND_STRL("result") TSRMLS_CC); zend_declare_class_constant_stringl(php_pqconn_class_entry, ZEND_STRL("EVENT_RESET"), ZEND_STRL("reset") TSRMLS_CC); zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("ASYNC"), 0x1 TSRMLS_CC); zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("PERSISTENT"), 0x2 TSRMLS_CC); return SUCCESS; } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <php.h> #include <libpq-events.h> #include "php_pq.h" #include "php_pq_misc.h" #include "php_pq_object.h" #include "php_pqconn_event.h" #include "php_pqres.h" static int apply_event(void *p, void *a TSRMLS_DC) { php_pq_callback_t *cb = p; zval *args = a; zval *retval = NULL; zend_fcall_info_args(&cb->fci, args TSRMLS_CC); zend_fcall_info_call(&cb->fci, &cb->fcc, &retval, NULL TSRMLS_CC); if (retval) { zval_ptr_dtor(&retval); } return ZEND_HASH_APPLY_KEEP; } static void php_pqconn_event_connreset(PGEventConnReset *event) { php_pqconn_event_data_t *data = PQinstanceData(event->conn, php_pqconn_event); if (data) { HashTable *evhs; TSRMLS_DF(data); if (SUCCESS == zend_hash_find(&data->obj->intern->eventhandlers, ZEND_STRS("reset"), (void *) &evhs)) { zval *args, *connection = NULL; MAKE_STD_ZVAL(args); array_init(args); php_pq_object_to_zval(data->obj, &connection TSRMLS_CC); add_next_index_zval(args, connection); zend_hash_apply_with_argument(evhs, apply_event, args TSRMLS_CC); zval_ptr_dtor(&args); } } } static void php_pqconn_event_resultcreate(PGEventResultCreate *event) { php_pqconn_event_data_t *data = PQinstanceData(event->conn, php_pqconn_event); if (data) { php_pqres_object_t *obj; HashTable *evhs; TSRMLS_DF(data); php_pqres_init_instance_data(event->result, data->obj, &obj TSRMLS_CC); /* event listener */ if (SUCCESS == zend_hash_find(&data->obj->intern->eventhandlers, ZEND_STRS("result"), (void *) &evhs)) { zval *args, *connection = NULL, *res = NULL; MAKE_STD_ZVAL(args); array_init(args); php_pq_object_to_zval(data->obj, &connection TSRMLS_CC); add_next_index_zval(args, connection); php_pq_object_to_zval(obj, &res TSRMLS_CC); add_next_index_zval(args, res); zend_hash_apply_with_argument(evhs, apply_event, args TSRMLS_CC); zval_ptr_dtor(&args); } /* async callback */ if (data->obj->intern->onevent.fci.size > 0) { zval *res = NULL; php_pq_object_to_zval(obj, &res TSRMLS_CC); zend_fcall_info_argn(&data->obj->intern->onevent.fci TSRMLS_CC, 1, &res); zend_fcall_info_call(&data->obj->intern->onevent.fci, &data->obj->intern->onevent.fcc, NULL, NULL TSRMLS_CC); zval_ptr_dtor(&res); } } } static void php_pqconn_event_resultdestroy(PGEventResultDestroy *event) { php_pqres_object_t *obj = PQresultInstanceData(event->result, php_pqconn_event); if (obj) { obj->intern->res = NULL; } } int php_pqconn_event(PGEventId id, void *e, void *data) { switch (id) { case PGEVT_CONNRESET: php_pqconn_event_connreset(e); break; case PGEVT_RESULTCREATE: php_pqconn_event_resultcreate(e); break; case PGEVT_RESULTDESTROY: php_pqconn_event_resultdestroy(e); break; default: break; } return 1; } php_pqconn_event_data_t *php_pqconn_event_data_init(php_pqconn_object_t *obj TSRMLS_DC) { php_pqconn_event_data_t *data = emalloc(sizeof(*data)); data->obj = obj; TSRMLS_CF(data); return data; } void php_pqconn_notice_recv(void *p, const PGresult *res) { php_pqconn_event_data_t *data = p; if (data) { HashTable *evhs; TSRMLS_DF(data); if (SUCCESS == zend_hash_find(&data->obj->intern->eventhandlers, ZEND_STRS("notice"), (void *) &evhs)) { zval *args, *connection = NULL; MAKE_STD_ZVAL(args); array_init(args); php_pq_object_to_zval(data->obj, &connection TSRMLS_CC); add_next_index_zval(args, connection); add_next_index_string(args, PHP_PQresultErrorMessage(res), 1); zend_hash_apply_with_argument(evhs, apply_event, args TSRMLS_CC); zval_ptr_dtor(&args); } } } void php_pqconn_notice_ignore(void *p, const PGresult *res) { } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifndef PHP_PQCONN_EVENT_H #define PHP_PQCONN_EVENT_H #include <libpq-events.h> #include "php_pqconn.h" typedef struct php_pqconn_event_data { php_pqconn_object_t *obj; #ifdef ZTS void ***ts; #endif } php_pqconn_event_data_t; extern php_pqconn_event_data_t *php_pqconn_event_data_init(php_pqconn_object_t *obj TSRMLS_DC); extern void php_pqconn_notice_recv(void *p, const PGresult *res); extern void php_pqconn_notice_ignore(void *p, const PGresult *res); extern int php_pqconn_event(PGEventId id, void *e, void *data); #endif /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifndef PHP_PQCONN_H #define PHP_PQCONN_H #define PHP_PQCONN_ASYNC 0x01 #define PHP_PQCONN_PERSISTENT 0x02 #include <ext/raphf/php_raphf.h> #include "php_pq_callback.h" #include "php_pq_params.h" typedef struct php_pqconn { PGconn *conn; int (*poller)(PGconn *); php_resource_factory_t factory; HashTable listeners; HashTable converters; HashTable eventhandlers; php_pq_callback_t onevent; unsigned unbuffered:1; unsigned default_fetch_type:2; unsigned default_txn_isolation:2; unsigned default_txn_readonly:1; unsigned default_txn_deferrable:1; unsigned default_auto_convert:16; } php_pqconn_t; typedef struct php_pqconn_object { zend_object zo; zend_object_value zv; HashTable *prophandler; php_pqconn_t *intern; } php_pqconn_object_t; typedef struct php_pqconn_resource_factory_data { char *dsn; long flags; } php_pqconn_resource_factory_data_t; extern php_resource_factory_ops_t *php_pqconn_get_resource_factory_ops(void); extern zend_class_entry *php_pqconn_class_entry; extern zend_object_value php_pqconn_create_object_ex(zend_class_entry *ce, php_pqconn_t *intern, php_pqconn_object_t **ptr TSRMLS_DC); extern void php_pqconn_notify_listeners(php_pqconn_object_t *obj TSRMLS_DC); extern STATUS php_pqconn_prepare(zval *object, php_pqconn_object_t *obj, const char *name, const char *query, php_pq_params_t *params TSRMLS_DC); extern STATUS php_pqconn_prepare_async(zval *object, php_pqconn_object_t *obj, const char *name, const char *query, php_pq_params_t *params TSRMLS_DC); extern STATUS php_pqconn_start_transaction(zval *zconn, php_pqconn_object_t *conn_obj, long isolation, zend_bool readonly, zend_bool deferrable TSRMLS_DC); extern STATUS php_pqconn_start_transaction_async(zval *zconn, php_pqconn_object_t *conn_obj, long isolation, zend_bool readonly, zend_bool deferrable TSRMLS_DC); extern STATUS php_pqconn_declare(zval *object, php_pqconn_object_t *obj, const char *decl TSRMLS_DC); extern STATUS php_pqconn_declare_async(zval *object, php_pqconn_object_t *obj, const char *decl TSRMLS_DC); extern PHP_MINIT_FUNCTION(pqconn); extern PHP_MSHUTDOWN_FUNCTION(pqconn); #endif /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <php.h> #include <ext/standard/php_smart_str.h> #include <libpq-events.h> #include "php_pq.h" #include "php_pq_misc.h" #include "php_pq_object.h" #include "php_pqexc.h" #include "php_pqres.h" #include "php_pqconn.h" #include "php_pqcopy.h" zend_class_entry *php_pqcopy_class_entry; static zend_object_handlers php_pqcopy_object_handlers; static HashTable php_pqcopy_object_prophandlers; static void php_pqcopy_object_free(void *o TSRMLS_DC) { php_pqcopy_object_t *obj = o; #if DBG_GC fprintf(stderr, "FREE copy(#%d) %p (conn(#%d): %p)\n", obj->zv.handle, obj, obj->intern->conn->zv.handle, obj->intern->conn); #endif if (obj->intern) { efree(obj->intern->expression); efree(obj->intern->options); php_pq_object_delref(obj->intern->conn TSRMLS_CC); efree(obj->intern); obj->intern = NULL; } zend_object_std_dtor((zend_object *) o TSRMLS_CC); efree(obj); } zend_object_value php_pqcopy_create_object_ex(zend_class_entry *ce, php_pqcopy_t *intern, php_pqcopy_object_t **ptr TSRMLS_DC) { php_pqcopy_object_t *o; o = ecalloc(1, sizeof(*o)); zend_object_std_init((zend_object *) o, ce TSRMLS_CC); object_properties_init((zend_object *) o, ce); o->prophandler = &php_pqcopy_object_prophandlers; if (ptr) { *ptr = o; } if (intern) { o->intern = intern; } o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqcopy_object_free, NULL TSRMLS_CC); o->zv.handlers = &php_pqcopy_object_handlers; return o->zv; } static zend_object_value php_pqcopy_create_object(zend_class_entry *class_type TSRMLS_DC) { return php_pqcopy_create_object_ex(class_type, NULL, NULL TSRMLS_CC); } static void php_pqcopy_object_read_connection(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqcopy_object_t *obj = o; php_pq_object_to_zval(obj->intern->conn, &return_value TSRMLS_CC); } static void php_pqcopy_object_read_direction(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqcopy_object_t *obj = o; RETVAL_LONG(obj->intern->direction); } static void php_pqcopy_object_read_expression(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqcopy_object_t *obj = o; RETURN_STRING(obj->intern->expression, 1); } static void php_pqcopy_object_read_options(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqcopy_object_t *obj = o; RETURN_STRING(obj->intern->options, 1); } ZEND_BEGIN_ARG_INFO_EX(ai_pqcopy_construct, 0, 0, 3) ZEND_ARG_OBJ_INFO(0, connection, pq\\Connection, 0) ZEND_ARG_INFO(0, expression) ZEND_ARG_INFO(0, direction) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO(); static PHP_METHOD(pqcopy, __construct) { zend_error_handling zeh; zval *zconn; char *expr_str, *opt_str = ""; int expr_len, opt_len = 0; long direction; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Osl|s", &zconn, php_pqconn_class_entry, &expr_str, &expr_len, &direction, &opt_str, &opt_len); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *conn_obj = zend_object_store_get_object(zconn TSRMLS_CC); if (!conn_obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { php_pqcopy_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); smart_str cmd = {0}; PGresult *res; smart_str_appends(&cmd, "COPY "); smart_str_appendl(&cmd, expr_str, expr_len); switch (direction) { case PHP_PQCOPY_FROM_STDIN: smart_str_appends(&cmd, " FROM STDIN "); break; case PHP_PQCOPY_TO_STDOUT: smart_str_appends(&cmd, " TO STDOUT "); break; default: throw_exce(EX_RUNTIME TSRMLS_CC, "Invalid COPY direction, expected one of FROM_STDIN (%d) TO_STDOUT (%d), got %ld", PHP_PQCOPY_FROM_STDIN, PHP_PQCOPY_TO_STDOUT, direction); smart_str_free(&cmd); return; } smart_str_appendl(&cmd, opt_str, opt_len); smart_str_0(&cmd); res = PQexec(conn_obj->intern->conn, cmd.c); if (!res) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to start %s (%s)", cmd.c, PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { obj->intern = ecalloc(1, sizeof(*obj->intern)); obj->intern->direction = direction; obj->intern->expression = estrdup(expr_str); obj->intern->options = estrdup(opt_str); obj->intern->conn = conn_obj; php_pq_object_addref(conn_obj TSRMLS_CC); } PHP_PQclear(res); } smart_str_free(&cmd); php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqcopy_put, 0, 0, 1) ZEND_ARG_INFO(0, data) ZEND_END_ARG_INFO(); static PHP_METHOD(pqcopy, put) { zend_error_handling zeh; char *data_str; int data_len; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data_str, &data_len); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqcopy_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\COPY not initialized"); } else if (obj->intern->direction != PHP_PQCOPY_FROM_STDIN) { throw_exce(EX_BAD_METHODCALL TSRMLS_CC, "pq\\COPY was not initialized with FROM_STDIN"); } else { if (1 != PQputCopyData(obj->intern->conn->intern->conn, data_str, data_len)) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to put COPY data (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqcopy_end, 0, 0, 0) ZEND_ARG_INFO(0, error) ZEND_END_ARG_INFO(); static PHP_METHOD(pqcopy, end) { zend_error_handling zeh; char *error_str = NULL; int error_len = 0; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &error_str, &error_len); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqcopy_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\COPY not intitialized"); } else if (obj->intern->direction != PHP_PQCOPY_FROM_STDIN) { throw_exce(EX_BAD_METHODCALL TSRMLS_CC, "pq\\COPY was not intitialized with FROM_STDIN"); } else { if (1 != PQputCopyEnd(obj->intern->conn->intern->conn, error_str)) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to end COPY (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { PGresult *res = PQgetResult(obj->intern->conn->intern->conn); if (!res) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to fetch COPY result (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { php_pqres_success(res TSRMLS_CC); PHP_PQclear(res); } } php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqcopy_get, 0, 0, 1) ZEND_ARG_INFO(1, data) ZEND_END_ARG_INFO(); static PHP_METHOD(pqcopy, get) { zend_error_handling zeh; zval *zdata; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zdata); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqcopy_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\COPY not initialized"); } else if (obj->intern->direction != PHP_PQCOPY_TO_STDOUT) { throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\COPY was not intialized with TO_STDOUT"); } else { PGresult *res; char *buffer = NULL; int bytes = PQgetCopyData(obj->intern->conn->intern->conn, &buffer, 0); switch (bytes) { case -2: throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to fetch COPY data (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); break; case -1: res = PQgetResult(obj->intern->conn->intern->conn); if (!res) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to fetch COPY result (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { php_pqres_success(res TSRMLS_CC); PHP_PQclear(res); RETVAL_FALSE; } break; default: zval_dtor(zdata); if (buffer) { ZVAL_STRINGL(zdata, buffer, bytes, 1); } else { ZVAL_EMPTY_STRING(zdata); } RETVAL_TRUE; break; } if (buffer) { PQfreemem(buffer); } } } } static zend_function_entry php_pqcopy_methods[] = { PHP_ME(pqcopy, __construct, ai_pqcopy_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) PHP_ME(pqcopy, put, ai_pqcopy_put, ZEND_ACC_PUBLIC) PHP_ME(pqcopy, end, ai_pqcopy_end, ZEND_ACC_PUBLIC) PHP_ME(pqcopy, get, ai_pqcopy_get, ZEND_ACC_PUBLIC) {0} }; PHP_MSHUTDOWN_FUNCTION(pqcopy) { zend_hash_destroy(&php_pqcopy_object_prophandlers); return SUCCESS; } PHP_MINIT_FUNCTION(pqcopy) { zend_class_entry ce = {0}; php_pq_object_prophandler_t ph = {0}; INIT_NS_CLASS_ENTRY(ce, "pq", "COPY", php_pqcopy_methods); php_pqcopy_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC); php_pqcopy_class_entry->create_object = php_pqcopy_create_object; memcpy(&php_pqcopy_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); php_pqcopy_object_handlers.read_property = php_pq_object_read_prop; php_pqcopy_object_handlers.write_property = php_pq_object_write_prop; php_pqcopy_object_handlers.clone_obj = NULL; php_pqcopy_object_handlers.get_property_ptr_ptr = NULL; php_pqcopy_object_handlers.get_gc = NULL; php_pqcopy_object_handlers.get_properties = php_pq_object_properties; php_pqcopy_object_handlers.get_debug_info = php_pq_object_debug_info; zend_hash_init(&php_pqcopy_object_prophandlers, 4, NULL, NULL, 1); zend_declare_property_null(php_pqcopy_class_entry, ZEND_STRL("connection"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqcopy_object_read_connection; zend_hash_add(&php_pqcopy_object_prophandlers, "connection", sizeof("connection"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_null(php_pqcopy_class_entry, ZEND_STRL("expression"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqcopy_object_read_expression; zend_hash_add(&php_pqcopy_object_prophandlers, "expression", sizeof("expression"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_null(php_pqcopy_class_entry, ZEND_STRL("direction"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqcopy_object_read_direction; zend_hash_add(&php_pqcopy_object_prophandlers, "direction", sizeof("direction"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_null(php_pqcopy_class_entry, ZEND_STRL("options"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqcopy_object_read_options; zend_hash_add(&php_pqcopy_object_prophandlers, "options", sizeof("options"), (void *) &ph, sizeof(ph), NULL); zend_declare_class_constant_long(php_pqcopy_class_entry, ZEND_STRL("FROM_STDIN"), PHP_PQCOPY_FROM_STDIN TSRMLS_CC); zend_declare_class_constant_long(php_pqcopy_class_entry, ZEND_STRL("TO_STDOUT"), PHP_PQCOPY_TO_STDOUT TSRMLS_CC); return SUCCESS; } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifndef PHP_PQCOPY_H #define PHP_PQCOPY_H #include "php_pqconn.h" typedef enum php_pqcopy_direction { PHP_PQCOPY_FROM_STDIN, PHP_PQCOPY_TO_STDOUT } php_pqcopy_direction_t; typedef enum php_pqcopy_status { PHP_PQCOPY_FAIL, PHP_PQCOPY_CONT, PHP_PQCOPY_DONE } php_pqcopy_status_t; typedef struct php_pqcopy { php_pqcopy_direction_t direction; char *expression; char *options; php_pqconn_object_t *conn; } php_pqcopy_t; typedef struct php_pqcopy_object { zend_object zo; zend_object_value zv; HashTable *prophandler; php_pqcopy_t *intern; } php_pqcopy_object_t; extern zend_class_entry *php_pqcopy_class_entry; extern zend_object_value php_pqcopy_create_object_ex(zend_class_entry *ce, php_pqcopy_t *intern, php_pqcopy_object_t **ptr TSRMLS_DC); extern PHP_MINIT_FUNCTION(pqcopy); extern PHP_MSHUTDOWN_FUNCTION(pqcopy); #endif /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <php.h> #include <ext/standard/php_smart_str.h> #include "php_pq.h" #include "php_pq_misc.h" #include "php_pq_object.h" #include "php_pqexc.h" #include "php_pqconn.h" #include "php_pqres.h" #include "php_pqcur.h" zend_class_entry *php_pqcur_class_entry; static zend_object_handlers php_pqcur_object_handlers; static HashTable php_pqcur_object_prophandlers; static void cur_close(php_pqcur_object_t *obj TSRMLS_DC) { if (obj->intern->open && obj->intern->conn->intern) { PGresult *res; smart_str cmd = {0}; smart_str_appends(&cmd, "CLOSE "); smart_str_appends(&cmd, obj->intern->name); smart_str_0(&cmd); if ((res = PQexec(obj->intern->conn->intern->conn, cmd.c))) { PHP_PQclear(res); } smart_str_free(&cmd); obj->intern->open = 0; } } static void cur_fetch_or_move(INTERNAL_FUNCTION_PARAMETERS, const char *action, zend_bool async) { char *spec_str = "1"; int spec_len = 1; STATUS rv; php_pq_callback_t resolver = {{0}}; zend_error_handling zeh; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, async ? "|sf" : "|s", &spec_str, &spec_len, &resolver.fci, &resolver.fcc); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqcur_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Cursor not initialized"); } else { smart_str cmd = {0}; smart_str_appends(&cmd, *action == 'f' ? "FETCH " : "MOVE "); smart_str_appendl(&cmd, spec_str, spec_len); smart_str_appends(&cmd, " FROM "); smart_str_appends(&cmd, obj->intern->name); smart_str_0(&cmd); if (async) { int rc = PQsendQuery(obj->intern->conn->intern->conn, cmd.c); if (!rc) { throw_exce(EX_IO TSRMLS_CC, "Failed to %s cursor (%s)", *action == 'f' ? "fetch from" : "move in", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); #if HAVE_PQSETSINGLEROWMODE } else if (obj->intern->conn->intern->unbuffered && !PQsetSingleRowMode(obj->intern->conn->intern->conn)) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); #endif } else { php_pq_callback_recurse(&obj->intern->conn->intern->onevent, &resolver TSRMLS_CC); obj->intern->conn->intern->poller = PQconsumeInput; } } else { PGresult *res = PQexec(obj->intern->conn->intern->conn, cmd.c); if (!res) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to %s cursor (%s)", *action == 'f' ? "fetch from" : "move in", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { php_pq_object_to_zval_no_addref(PQresultInstanceData(res, php_pqconn_event), &return_value TSRMLS_CC); } } smart_str_free(&cmd); php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } } static void php_pqcur_object_free(void *o TSRMLS_DC) { php_pqcur_object_t *obj = o; #if DBG_GC fprintf(stderr, "FREE cur(#%d) %p (conn: %p)\n", obj->zv.handle, obj, obj->intern->conn); #endif if (obj->intern) { cur_close(obj TSRMLS_CC); php_pq_object_delref(obj->intern->conn TSRMLS_CC); efree(obj->intern->decl); efree(obj->intern->name); efree(obj->intern); obj->intern = NULL; } zend_object_std_dtor((zend_object *) o TSRMLS_CC); efree(obj); } zend_object_value php_pqcur_create_object_ex(zend_class_entry *ce, php_pqcur_t *intern, php_pqcur_object_t **ptr TSRMLS_DC) { php_pqcur_object_t *o; o = ecalloc(1, sizeof(*o)); zend_object_std_init((zend_object *) o, ce TSRMLS_CC); object_properties_init((zend_object *) o, ce); o->prophandler = &php_pqcur_object_prophandlers; if (ptr) { *ptr = o; } if (intern) { o->intern = intern; } o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqcur_object_free, NULL TSRMLS_CC); o->zv.handlers = &php_pqcur_object_handlers; return o->zv; } static zend_object_value php_pqcur_create_object(zend_class_entry *class_type TSRMLS_DC) { return php_pqcur_create_object_ex(class_type, NULL, NULL TSRMLS_CC); } static void php_pqcur_object_read_name(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqcur_object_t *obj = o; RETVAL_STRING(obj->intern->name, 1); } static void php_pqcur_object_read_connection(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqcur_object_t *obj = o; php_pq_object_to_zval(obj->intern->conn, &return_value TSRMLS_CC); } char *php_pqcur_declare_str(const char *name_str, size_t name_len, unsigned flags, const char *query_str, size_t query_len) { size_t decl_len = name_len + query_len + sizeof("DECLARE BINARY INSENSITIVE NO SCROLL CURSOR WITHOUT HOLD FOR "); char *decl_str; decl_str = emalloc(decl_len); decl_len = slprintf(decl_str, decl_len, "DECLARE %s %s %s %s CURSOR %s FOR %s", name_str, (flags & PHP_PQ_DECLARE_BINARY) ? "BINARY" : "", (flags & PHP_PQ_DECLARE_INSENSITIVE) ? "INSENSITIVE" : "", (flags & PHP_PQ_DECLARE_NO_SCROLL) ? "NO SCROLL" : (flags & PHP_PQ_DECLARE_SCROLL) ? "SCROLL" : "", (flags & PHP_PQ_DECLARE_WITH_HOLD) ? "WITH HOLD" : "", query_str ); return decl_str; } ZEND_BEGIN_ARG_INFO_EX(ai_pqcur___construct, 0, 0, 4) ZEND_ARG_OBJ_INFO(0, connection, pq\\Connection, 0) ZEND_ARG_INFO(0, name) ZEND_ARG_INFO(0, flags) ZEND_ARG_INFO(0, query) ZEND_ARG_INFO(0, async) ZEND_END_ARG_INFO(); static PHP_METHOD(pqcur, __construct) { zend_error_handling zeh; char *name_str, *query_str; int name_len, query_len; long flags; zval *zconn; STATUS rv; zend_bool async = 0; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Osls|b", &zconn, php_pqconn_class_entry, &name_str, &name_len, &flags, &query_str, &query_len, &async); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqcur_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); php_pqconn_object_t *conn_obj = zend_object_store_get_object(zconn TSRMLS_CC); if (obj->intern) { throw_exce(EX_BAD_METHODCALL TSRMLS_CC, "pq\\Cursor already initialized"); } if (!conn_obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { char *decl = php_pqcur_declare_str(name_str, name_len, flags, query_str, query_len); if (async) { rv = php_pqconn_declare_async(zconn, conn_obj, decl TSRMLS_CC); } else { rv = php_pqconn_declare(zconn, conn_obj, decl TSRMLS_CC); } if (SUCCESS != rv) { efree(decl); } else { php_pqcur_t *cur = ecalloc(1, sizeof(*cur)); php_pq_object_addref(conn_obj TSRMLS_CC); cur->conn = conn_obj; cur->open = 1; cur->name = estrdup(name_str); cur->decl = decl; obj->intern = cur; } } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_open, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqcur, open) { zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters_none(); zend_restore_error_handling(&zeh TSRMLS_CC); if (rv == SUCCESS) { php_pqcur_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Cursor not initialized"); } else if (!obj->intern->open) { if (SUCCESS == php_pqconn_declare(NULL, obj->intern->conn, obj->intern->decl TSRMLS_CC)) { obj->intern->open = 1; } } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_close, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqcur, close) { zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters_none(); zend_restore_error_handling(&zeh TSRMLS_CC); if (rv == SUCCESS) { php_pqcur_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Cursor not initialized"); } else { cur_close(obj TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_fetch, 0, 0, 1) ZEND_ARG_INFO(0, spec) ZEND_END_ARG_INFO(); static PHP_METHOD(pqcur, fetch) { cur_fetch_or_move(INTERNAL_FUNCTION_PARAM_PASSTHRU, "fetch", 0); } ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_move, 0, 0, 0) ZEND_ARG_INFO(0, spec) ZEND_END_ARG_INFO(); static PHP_METHOD(pqcur, move) { cur_fetch_or_move(INTERNAL_FUNCTION_PARAM_PASSTHRU, "move", 0); } ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_fetchAsync, 0, 0, 0) ZEND_ARG_INFO(0, spec) ZEND_ARG_INFO(0, callback) ZEND_END_ARG_INFO(); static PHP_METHOD(pqcur, fetchAsync) { cur_fetch_or_move(INTERNAL_FUNCTION_PARAM_PASSTHRU, "fetch", 1); } ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_moveAsync, 0, 0, 0) ZEND_ARG_INFO(0, spec) ZEND_ARG_INFO(0, callback) ZEND_END_ARG_INFO(); static PHP_METHOD(pqcur, moveAsync) { cur_fetch_or_move(INTERNAL_FUNCTION_PARAM_PASSTHRU, "move", 1); } static zend_function_entry php_pqcur_methods[] = { PHP_ME(pqcur, __construct, ai_pqcur___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) PHP_ME(pqcur, open, ai_pqcur_open, ZEND_ACC_PUBLIC) PHP_ME(pqcur, close, ai_pqcur_close, ZEND_ACC_PUBLIC) PHP_ME(pqcur, fetch, ai_pqcur_fetch, ZEND_ACC_PUBLIC) PHP_ME(pqcur, move, ai_pqcur_move, ZEND_ACC_PUBLIC) PHP_ME(pqcur, fetchAsync, ai_pqcur_fetchAsync, ZEND_ACC_PUBLIC) PHP_ME(pqcur, moveAsync, ai_pqcur_moveAsync, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; PHP_MSHUTDOWN_FUNCTION(pqcur) { zend_hash_destroy(&php_pqcur_object_prophandlers); return SUCCESS; } PHP_MINIT_FUNCTION(pqcur) { zend_class_entry ce = {0}; php_pq_object_prophandler_t ph = {0}; INIT_NS_CLASS_ENTRY(ce, "pq", "Cursor", php_pqcur_methods); php_pqcur_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC); php_pqcur_class_entry->create_object = php_pqcur_create_object; memcpy(&php_pqcur_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); php_pqcur_object_handlers.read_property = php_pq_object_read_prop; php_pqcur_object_handlers.write_property = php_pq_object_write_prop; php_pqcur_object_handlers.clone_obj = NULL; php_pqcur_object_handlers.get_property_ptr_ptr = NULL; php_pqcur_object_handlers.get_gc = NULL; php_pqcur_object_handlers.get_properties = php_pq_object_properties; php_pqcur_object_handlers.get_debug_info = php_pq_object_debug_info; zend_hash_init(&php_pqcur_object_prophandlers, 2, NULL, NULL, 1); zend_declare_class_constant_long(php_pqcur_class_entry, ZEND_STRL("BINARY"), PHP_PQ_DECLARE_BINARY TSRMLS_CC); zend_declare_class_constant_long(php_pqcur_class_entry, ZEND_STRL("INSENSITIVE"), PHP_PQ_DECLARE_INSENSITIVE TSRMLS_CC); zend_declare_class_constant_long(php_pqcur_class_entry, ZEND_STRL("WITH_HOLD"), PHP_PQ_DECLARE_WITH_HOLD TSRMLS_CC); zend_declare_class_constant_long(php_pqcur_class_entry, ZEND_STRL("SCROLL"), PHP_PQ_DECLARE_SCROLL TSRMLS_CC); zend_declare_class_constant_long(php_pqcur_class_entry, ZEND_STRL("NO_SCROLL"), PHP_PQ_DECLARE_NO_SCROLL TSRMLS_CC); zend_declare_property_null(php_pqcur_class_entry, ZEND_STRL("name"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqcur_object_read_name; zend_hash_add(&php_pqcur_object_prophandlers, "name", sizeof("name"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_null(php_pqcur_class_entry, ZEND_STRL("connection"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqcur_object_read_connection; zend_hash_add(&php_pqcur_object_prophandlers, "connection", sizeof("connection"), (void *) &ph, sizeof(ph), NULL); return SUCCESS; } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifndef PHP_PQCUR_H #define PHP_PQCUR_H #include "php_pqconn.h" #define PHP_PQ_DECLARE_BINARY 0x01 #define PHP_PQ_DECLARE_INSENSITIVE 0x02 #define PHP_PQ_DECLARE_WITH_HOLD 0x04 #define PHP_PQ_DECLARE_SCROLL 0x10 #define PHP_PQ_DECLARE_NO_SCROLL 0x20 typedef struct php_pqcur { php_pqconn_object_t *conn; char *name; char *decl; unsigned open:1; } php_pqcur_t; typedef struct php_pqcur_object { zend_object zo; zend_object_value zv; HashTable *prophandler; php_pqcur_t *intern; } php_pqcur_object_t; extern zend_class_entry *php_pqcur_class_entry; extern zend_object_value php_pqcur_create_object_ex(zend_class_entry *ce, php_pqcur_t *intern, php_pqcur_object_t **ptr TSRMLS_DC); extern char *php_pqcur_declare_str(const char *name_str, size_t name_len, unsigned flags, const char *query_str, size_t query_len); extern PHP_MINIT_FUNCTION(pqcur); extern PHP_MSHUTDOWN_FUNCTION(pqcur); #endif /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <php.h> #include <Zend/zend_exceptions.h> #include <ext/spl/spl_exceptions.h> #include "php_pq.h" #include "php_pq_object.h" #include "php_pqexc.h" static zend_class_entry *php_pqexc_interface_class_entry; static zend_class_entry *php_pqexc_invalid_argument_class_entry; static zend_class_entry *php_pqexc_runtime_class_entry; static zend_class_entry *php_pqexc_bad_methodcall_class_entry; static zend_class_entry *php_pqexc_domain_class_entry; static zend_function_entry php_pqexc_methods[] = { {0} }; zend_class_entry *exce(php_pqexc_type_t type) { switch (type) { default: case EX_INVALID_ARGUMENT: return php_pqexc_invalid_argument_class_entry; case EX_RUNTIME: case EX_CONNECTION_FAILED: case EX_IO: case EX_ESCAPE: return php_pqexc_runtime_class_entry; case EX_UNINITIALIZED: case EX_BAD_METHODCALL: return php_pqexc_bad_methodcall_class_entry; case EX_DOMAIN: case EX_SQL: return php_pqexc_domain_class_entry; } } zval *throw_exce(php_pqexc_type_t type TSRMLS_DC, const char *fmt, ...) { char *msg; zval *zexc; va_list argv; va_start(argv, fmt); vspprintf(&msg, 0, fmt, argv); va_end(argv); zexc = zend_throw_exception(exce(type), msg, type TSRMLS_CC); efree(msg); return zexc; } PHP_MINIT_FUNCTION(pqexc) { zend_class_entry ce = {0}; INIT_NS_CLASS_ENTRY(ce, "pq", "Exception", php_pqexc_methods); php_pqexc_interface_class_entry = zend_register_internal_interface(&ce TSRMLS_CC); zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("INVALID_ARGUMENT"), EX_INVALID_ARGUMENT TSRMLS_CC); zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("RUNTIME"), EX_RUNTIME TSRMLS_CC); zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("CONNECTION_FAILED"), EX_CONNECTION_FAILED TSRMLS_CC); zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("IO"), EX_IO TSRMLS_CC); zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("ESCAPE"), EX_ESCAPE TSRMLS_CC); zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("BAD_METHODCALL"), EX_BAD_METHODCALL TSRMLS_CC); zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("UNINITIALIZED"), EX_UNINITIALIZED TSRMLS_CC); zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("DOMAIN"), EX_DOMAIN TSRMLS_CC); zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("SQL"), EX_SQL TSRMLS_CC); memset(&ce, 0, sizeof(ce)); INIT_NS_CLASS_ENTRY(ce, "pq\\Exception", "InvalidArgumentException", php_pqexc_methods); php_pqexc_invalid_argument_class_entry = zend_register_internal_class_ex(&ce, spl_ce_InvalidArgumentException, "InvalidArgumentException" TSRMLS_CC); zend_class_implements(php_pqexc_invalid_argument_class_entry TSRMLS_CC, 1, php_pqexc_interface_class_entry); memset(&ce, 0, sizeof(ce)); INIT_NS_CLASS_ENTRY(ce, "pq\\Exception", "RuntimeException", php_pqexc_methods); php_pqexc_runtime_class_entry = zend_register_internal_class_ex(&ce, spl_ce_RuntimeException, "RuntimeException" TSRMLS_CC); zend_class_implements(php_pqexc_runtime_class_entry TSRMLS_CC, 1, php_pqexc_interface_class_entry); memset(&ce, 0, sizeof(ce)); INIT_NS_CLASS_ENTRY(ce, "pq\\Exception", "BadMethodCallException", php_pqexc_methods); php_pqexc_bad_methodcall_class_entry = zend_register_internal_class_ex(&ce, spl_ce_BadMethodCallException, "BadMethodCallException" TSRMLS_CC); zend_class_implements(php_pqexc_bad_methodcall_class_entry TSRMLS_CC, 1, php_pqexc_interface_class_entry); memset(&ce, 0, sizeof(ce)); INIT_NS_CLASS_ENTRY(ce, "pq\\Exception", "DomainException", php_pqexc_methods); php_pqexc_domain_class_entry = zend_register_internal_class_ex(&ce, spl_ce_DomainException, "DomainException" TSRMLS_CC); zend_class_implements(php_pqexc_domain_class_entry TSRMLS_CC, 1, php_pqexc_interface_class_entry); zend_declare_property_null(php_pqexc_domain_class_entry, ZEND_STRL("sqlstate"), ZEND_ACC_PUBLIC TSRMLS_CC); return SUCCESS; } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifndef PHP_PQEXC_H #define PHP_PQEXC_H typedef enum php_pqexc_type { EX_INVALID_ARGUMENT, EX_RUNTIME, EX_CONNECTION_FAILED, EX_IO, EX_ESCAPE, EX_BAD_METHODCALL, EX_UNINITIALIZED, EX_DOMAIN, EX_SQL } php_pqexc_type_t; extern zend_class_entry *exce(php_pqexc_type_t type); extern zval *throw_exce(php_pqexc_type_t type TSRMLS_DC, const char *fmt, ...); extern PHP_MINIT_FUNCTION(pqexc); #endif /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <libpq-events.h> #include <libpq/libpq-fs.h> #include <php.h> #include "php_pq.h" #include "php_pq_misc.h" #include "php_pq_object.h" #include "php_pqexc.h" #include "php_pqlob.h" zend_class_entry *php_pqlob_class_entry; static zend_object_handlers php_pqlob_object_handlers; static HashTable php_pqlob_object_prophandlers; static void php_pqlob_object_free(void *o TSRMLS_DC) { php_pqlob_object_t *obj = o; #if DBG_GC fprintf(stderr, "FREE lob(#%d) %p (txn(#%d): %p)\n", obj->zv.handle, obj, obj->intern->txn->zv.handle, obj->intern->txn); #endif if (obj->intern) { if (obj->intern->lofd) { lo_close(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd); } /* invalidate the stream */ if (obj->intern->stream) { zend_list_delete(obj->intern->stream); obj->intern->stream = 0; } php_pq_object_delref(obj->intern->txn TSRMLS_CC); efree(obj->intern); obj->intern = NULL; } zend_object_std_dtor((zend_object *) o TSRMLS_CC); efree(obj); } zend_object_value php_pqlob_create_object_ex(zend_class_entry *ce, php_pqlob_t *intern, php_pqlob_object_t **ptr TSRMLS_DC) { php_pqlob_object_t *o; o = ecalloc(1, sizeof(*o)); zend_object_std_init((zend_object *) o, ce TSRMLS_CC); object_properties_init((zend_object *) o, ce); o->prophandler = &php_pqlob_object_prophandlers; if (ptr) { *ptr = o; } if (intern) { o->intern = intern; } o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqlob_object_free, NULL TSRMLS_CC); o->zv.handlers = &php_pqlob_object_handlers; return o->zv; } static zend_object_value php_pqlob_create_object(zend_class_entry *class_type TSRMLS_DC) { return php_pqlob_create_object_ex(class_type, NULL, NULL TSRMLS_CC); } static void php_pqlob_object_read_transaction(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqlob_object_t *obj = o; php_pq_object_to_zval(obj->intern->txn, &return_value TSRMLS_CC); } static void php_pqlob_object_read_oid(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqlob_object_t *obj = o; RETVAL_LONG(obj->intern->loid); } static void php_pqlob_object_update_stream(zval *this_ptr, php_pqlob_object_t *obj, zval **zstream TSRMLS_DC); static void php_pqlob_object_read_stream(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqlob_object_t *obj = o; if (!obj->intern->stream) { zval *zstream; php_pqlob_object_update_stream(object, obj, &zstream TSRMLS_CC); RETVAL_ZVAL(zstream, 1, 1); } else { RETVAL_RESOURCE(obj->intern->stream); zend_list_addref(obj->intern->stream); } } static size_t php_pqlob_stream_write(php_stream *stream, const char *buffer, size_t length TSRMLS_DC) { php_pqlob_object_t *obj = stream->abstract; int written = 0; if (obj) { written = lo_write(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, buffer, length); if (written < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to write to LOB with oid=%u (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn)); } php_pqconn_notify_listeners(obj->intern->txn->intern->conn TSRMLS_CC); } return written; } static size_t php_pqlob_stream_read(php_stream *stream, char *buffer, size_t length TSRMLS_DC) { php_pqlob_object_t *obj = stream->abstract; int read = 0; if (obj) { if (!buffer && !length) { if (lo_tell(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd) == lo_lseek(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, 0, SEEK_CUR)) { return EOF; } } else { read = lo_read(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, buffer, length); if (read < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to read from LOB with oid=%d (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn)); } } php_pqconn_notify_listeners(obj->intern->txn->intern->conn TSRMLS_CC); } return read; } static STATUS php_pqlob_stream_close(php_stream *stream, int close_handle TSRMLS_DC) { return SUCCESS; } static int php_pqlob_stream_flush(php_stream *stream TSRMLS_DC) { return SUCCESS; } static STATUS php_pqlob_stream_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) { STATUS rv = FAILURE; php_pqlob_object_t *obj = stream->abstract; if (obj) { int position = lo_lseek(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, offset, whence); if (position < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to seek offset in LOB with oid=%d (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn)); rv = FAILURE; } else { *newoffset = position; rv = SUCCESS; } php_pqconn_notify_listeners(obj->intern->txn->intern->conn TSRMLS_CC); } return rv; } static php_stream_ops php_pqlob_stream_ops = { /* stdio like functions - these are mandatory! */ php_pqlob_stream_write, php_pqlob_stream_read, php_pqlob_stream_close, php_pqlob_stream_flush, "pq\\LOB stream", /* these are optional */ php_pqlob_stream_seek, NULL, /* cast */ NULL, /* stat */ NULL, /* set_option */ }; static void php_pqlob_object_update_stream(zval *this_ptr, php_pqlob_object_t *obj, zval **zstream_ptr TSRMLS_DC) { zval *zstream, zmember; php_stream *stream; INIT_PZVAL(&zmember); ZVAL_STRINGL(&zmember, "stream", sizeof("stream")-1, 0); MAKE_STD_ZVAL(zstream); if (!obj) { obj = zend_object_store_get_object(getThis() TSRMLS_CC); } stream = php_stream_alloc(&php_pqlob_stream_ops, obj, NULL, "r+b"); stream->flags |= PHP_STREAM_FLAG_NO_FCLOSE; zend_list_addref(obj->intern->stream = stream->rsrc_id); php_stream_to_zval(stream, zstream); zend_get_std_object_handlers()->write_property(getThis(), &zmember, zstream, NULL TSRMLS_CC); if (zstream_ptr) { *zstream_ptr = zstream; } else { zval_ptr_dtor(&zstream); } } ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_construct, 0, 0, 1) ZEND_ARG_OBJ_INFO(0, transaction, pq\\Transaction, 0) ZEND_ARG_INFO(0, oid) ZEND_ARG_INFO(0, mode) ZEND_END_ARG_INFO(); static PHP_METHOD(pqlob, __construct) { zend_error_handling zeh; zval *ztxn; long mode = INV_WRITE|INV_READ, loid = InvalidOid; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|ll", &ztxn, php_pqtxn_class_entry, &loid, &mode); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqlob_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); php_pqtxn_object_t *txn_obj = zend_object_store_get_object(ztxn TSRMLS_CC); if (obj->intern) { throw_exce(EX_BAD_METHODCALL TSRMLS_CC, "pq\\LOB already initialized"); } else if (!txn_obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); } else if (!txn_obj->intern->open) { throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transation already closed"); } else { if (loid == InvalidOid) { loid = lo_creat(txn_obj->intern->conn->intern->conn, mode); } if (loid == InvalidOid) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to create large object with mode '%s' (%s)", php_pq_strmode(mode), PHP_PQerrorMessage(txn_obj->intern->conn->intern->conn)); } else { int lofd = lo_open(txn_obj->intern->conn->intern->conn, loid, mode); if (lofd < 0) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to open large object with oid=%u with mode '%s' (%s)", loid, php_pq_strmode(mode), PHP_PQerrorMessage(txn_obj->intern->conn->intern->conn)); } else { obj->intern = ecalloc(1, sizeof(*obj->intern)); obj->intern->lofd = lofd; obj->intern->loid = loid; php_pq_object_addref(txn_obj TSRMLS_CC); obj->intern->txn = txn_obj; } } php_pqconn_notify_listeners(txn_obj->intern->conn TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_write, 0, 0, 1) ZEND_ARG_INFO(0, data) ZEND_END_ARG_INFO(); static PHP_METHOD(pqlob, write) { zend_error_handling zeh; char *data_str; int data_len; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data_str, &data_len); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqlob_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\LOB not initialized"); } else { int written = lo_write(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, data_str, data_len); if (written < 0) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to write to LOB with oid=%u (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn)); } else { RETVAL_LONG(written); } php_pqconn_notify_listeners(obj->intern->txn->intern->conn TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_read, 0, 0, 0) ZEND_ARG_INFO(0, length) ZEND_ARG_INFO(1, read) ZEND_END_ARG_INFO(); static PHP_METHOD(pqlob, read) { zend_error_handling zeh; long length = 0x1000; zval *zread = NULL; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lz!", &length, &zread); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqlob_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\LOB not initialized"); } else { char *buffer = emalloc(length + 1); int read = lo_read(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, buffer, length); if (read < 0) { efree(buffer); throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to read from LOB with oid=%d (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn)); } else { if (zread) { zval_dtor(zread); ZVAL_LONG(zread, read); } buffer[read] = '\0'; RETVAL_STRINGL(buffer, read, 0); } php_pqconn_notify_listeners(obj->intern->txn->intern->conn TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_seek, 0, 0, 1) ZEND_ARG_INFO(0, offset) ZEND_ARG_INFO(0, whence) ZEND_END_ARG_INFO(); static PHP_METHOD(pqlob, seek) { zend_error_handling zeh; long offset, whence = SEEK_SET; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|l", &offset, &whence); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqlob_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\LOB not initialized"); } else { int position = lo_lseek(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, offset, whence); if (position < 0) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to seek offset in LOB with oid=%d (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn)); } else { RETVAL_LONG(position); } php_pqconn_notify_listeners(obj->intern->txn->intern->conn TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_tell, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqlob, tell) { zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters_none(); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqlob_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\LOB not initialized"); } else { int position = lo_tell(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd); if (position < 0) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to tell offset in LOB with oid=%d (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn)); } else { RETVAL_LONG(position); } php_pqconn_notify_listeners(obj->intern->txn->intern->conn TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_truncate, 0, 0, 0) ZEND_ARG_INFO(0, length) ZEND_END_ARG_INFO(); static PHP_METHOD(pqlob, truncate) { zend_error_handling zeh; long length = 0; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &length); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqlob_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\LOB not initialized"); } else { int rc = lo_truncate(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, length); if (rc != 0) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to truncate LOB with oid=%d (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn)); } php_pqconn_notify_listeners(obj->intern->txn->intern->conn TSRMLS_CC); } } } static zend_function_entry php_pqlob_methods[] = { PHP_ME(pqlob, __construct, ai_pqlob_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) PHP_ME(pqlob, write, ai_pqlob_write, ZEND_ACC_PUBLIC) PHP_ME(pqlob, read, ai_pqlob_read, ZEND_ACC_PUBLIC) PHP_ME(pqlob, seek, ai_pqlob_seek, ZEND_ACC_PUBLIC) PHP_ME(pqlob, tell, ai_pqlob_tell, ZEND_ACC_PUBLIC) PHP_ME(pqlob, truncate, ai_pqlob_truncate, ZEND_ACC_PUBLIC) {0} }; PHP_MSHUTDOWN_FUNCTION(pqlob) { zend_hash_destroy(&php_pqlob_object_prophandlers); return SUCCESS; } PHP_MINIT_FUNCTION(pqlob) { zend_class_entry ce = {0}; php_pq_object_prophandler_t ph = {0}; INIT_NS_CLASS_ENTRY(ce, "pq", "LOB", php_pqlob_methods); php_pqlob_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC); php_pqlob_class_entry->create_object = php_pqlob_create_object; memcpy(&php_pqlob_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); php_pqlob_object_handlers.read_property = php_pq_object_read_prop; php_pqlob_object_handlers.write_property = php_pq_object_write_prop; php_pqlob_object_handlers.clone_obj = NULL; php_pqlob_object_handlers.get_property_ptr_ptr = NULL; php_pqlob_object_handlers.get_gc = NULL; php_pqlob_object_handlers.get_properties = php_pq_object_properties; php_pqlob_object_handlers.get_debug_info = php_pq_object_debug_info; zend_hash_init(&php_pqlob_object_prophandlers, 3, NULL, NULL, 1); zend_declare_property_null(php_pqlob_class_entry, ZEND_STRL("transaction"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqlob_object_read_transaction; zend_hash_add(&php_pqlob_object_prophandlers, "transaction", sizeof("transaction"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_long(php_pqlob_class_entry, ZEND_STRL("oid"), InvalidOid, ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqlob_object_read_oid; zend_hash_add(&php_pqlob_object_prophandlers, "oid", sizeof("oid"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_null(php_pqlob_class_entry, ZEND_STRL("stream"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqlob_object_read_stream; zend_hash_add(&php_pqlob_object_prophandlers, "stream", sizeof("stream"), (void *) &ph, sizeof(ph), NULL); zend_declare_class_constant_long(php_pqlob_class_entry, ZEND_STRL("INVALID_OID"), InvalidOid TSRMLS_CC); zend_declare_class_constant_long(php_pqlob_class_entry, ZEND_STRL("R"), INV_READ TSRMLS_CC); zend_declare_class_constant_long(php_pqlob_class_entry, ZEND_STRL("W"), INV_WRITE TSRMLS_CC); zend_declare_class_constant_long(php_pqlob_class_entry, ZEND_STRL("RW"), INV_READ|INV_WRITE TSRMLS_CC); return SUCCESS; } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifndef PHP_PQLOB_H #define PHP_PQLOB_H #include "php_pqtxn.h" typedef struct php_pqlob { int lofd; Oid loid; int stream; php_pqtxn_object_t *txn; } php_pqlob_t; typedef struct php_pqlob_object { zend_object zo; zend_object_value zv; HashTable *prophandler; php_pqlob_t *intern; } php_pqlob_object_t; extern zend_class_entry *php_pqlob_class_entry; extern zend_object_value php_pqlob_create_object_ex(zend_class_entry *ce, php_pqlob_t *intern, php_pqlob_object_t **ptr TSRMLS_DC); extern PHP_MINIT_FUNCTION(pqlob); extern PHP_MSHUTDOWN_FUNCTION(pqlob); #endif /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <php.h> #include <ext/date/php_date.h> #include <ext/standard/php_string.h> #include <Zend/zend_interfaces.h> #include <libpq/libpq-fs.h> #include "php_pq.h" #include "php_pqexc.h" #include "php_pq_misc.h" char *php_pq_rtrim(char *e) { size_t l = strlen(e); while (l-- > 0 && e[l] == '\n') { e[l] = '\0'; } return e; } const char *php_pq_strmode(long mode) { switch (mode & (INV_READ|INV_WRITE)) { case INV_READ|INV_WRITE: return "rw"; case INV_READ: return "r"; case INV_WRITE: return "w"; default: return "-"; } } int php_pq_compare_index(const void *lptr, const void *rptr TSRMLS_DC) { const Bucket *l = *(const Bucket **) lptr; const Bucket *r = *(const Bucket **) rptr; if (l->h < r->h) { return -1; } if (l->h > r->h) { return 1; } return 0; } zend_class_entry *php_pqdt_class_entry; ZEND_BEGIN_ARG_INFO_EX(ai_pqdt_to_string, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqdt, __toString) { zval *rv = NULL; zend_call_method_with_1_params(&getThis(), php_pqdt_class_entry, NULL, "format", &rv, zend_read_property(php_pqdt_class_entry, getThis(), ZEND_STRL("format"), 0 TSRMLS_CC)); if (rv) { RETVAL_ZVAL(rv, 1, 1); } } ZEND_BEGIN_ARG_INFO_EX(ai_pqdt_create_from_format, 0, 0, 2) ZEND_ARG_INFO(0, format) ZEND_ARG_INFO(0, datetime) /* ZEND_ARG_OBJ_INFO(0, timezone, DateTimezone, 1) date's arginfo is not specific */ ZEND_ARG_INFO(0, timezone) ZEND_END_ARG_INFO(); static PHP_METHOD(pqdt, createFromFormat) { zend_error_handling zeh; char *fmt_str, *dt_str; int fmt_len, dt_len; zval *ztz = NULL; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|O", &fmt_str, &fmt_len, &dt_str, &dt_len, &ztz, php_date_get_timezone_ce()); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqdt_from_string(return_value, fmt_str, dt_str, dt_len, "Y-m-d H:i:s.uO", ztz TSRMLS_CC); } } static zend_function_entry php_pqdt_methods[] = { PHP_ME(pqdt, createFromFormat, ai_pqdt_create_from_format, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) PHP_ME(pqdt, __toString, ai_pqdt_to_string, ZEND_ACC_PUBLIC) PHP_MALIAS(pqdt, jsonSerialize, __toString, ai_pqdt_to_string, ZEND_ACC_PUBLIC) {0} }; zval *php_pqdt_from_string(zval *zv, char *input_fmt, char *dt_str, size_t dt_len, char *output_fmt, zval *ztimezone TSRMLS_DC) { php_date_obj *dobj; if (!zv) { MAKE_STD_ZVAL(zv); } php_date_instantiate(php_pqdt_class_entry, zv TSRMLS_CC); dobj = zend_object_store_get_object(zv TSRMLS_CC); if (!php_date_initialize(dobj, dt_str, dt_len, input_fmt, ztimezone, 1 TSRMLS_CC)) { zval_dtor(zv); ZVAL_NULL(zv); } else if (output_fmt) { zend_update_property_string(php_pqdt_class_entry, zv, ZEND_STRL("format"), output_fmt TSRMLS_CC); } return zv; } void php_pqdt_to_string(zval *zdt, const char *format, char **str_buf, size_t *str_len TSRMLS_DC) { zval rv; INIT_PZVAL(&rv); ZVAL_NULL(&rv); if (Z_OBJ_HT_P(zdt)->cast_object && SUCCESS == Z_OBJ_HT_P(zdt)->cast_object(zdt, &rv, IS_STRING TSRMLS_CC) ) { *str_len = Z_STRLEN(rv); *str_buf = Z_STRVAL(rv); } else if (instanceof_function(Z_OBJCE_P(zdt), php_date_get_date_ce() TSRMLS_CC)) { zval *rv = NULL, *zfmt; MAKE_STD_ZVAL(zfmt); ZVAL_STRING(zfmt, format, 1); zend_call_method_with_1_params(&zdt, Z_OBJCE_P(zdt), NULL, "format", &rv, zfmt); zval_ptr_dtor(&zfmt); if (rv) { if (Z_TYPE_P(rv) == IS_STRING) { *str_len = Z_STRLEN_P(rv); *str_buf = estrndup(Z_STRVAL_P(rv), *str_len); } zval_ptr_dtor(&rv); } } } zend_class_entry *php_pqconv_class_entry; ZEND_BEGIN_ARG_INFO_EX(ai_pqconv_convert_types, 0, 0, 0) ZEND_END_ARG_INFO(); ZEND_BEGIN_ARG_INFO_EX(ai_pqconv_convert_from_string, 0, 0, 2) ZEND_ARG_INFO(0, data) ZEND_ARG_INFO(0, type) ZEND_END_ARG_INFO(); ZEND_BEGIN_ARG_INFO_EX(ai_pqconv_convert_to_string, 0, 0, 2) ZEND_ARG_INFO(0, data) ZEND_ARG_INFO(0, type) ZEND_END_ARG_INFO(); zend_function_entry php_pqconv_methods[] = { PHP_ABSTRACT_ME(pqconv, convertTypes, ai_pqconv_convert_types) PHP_ABSTRACT_ME(pqconv, convertFromString, ai_pqconv_convert_from_string) PHP_ABSTRACT_ME(pqconv, convertToString, ai_pqconv_convert_to_string) {0} }; PHP_MINIT_FUNCTION(pq_misc) { zend_class_entry **json, ce = {0}; INIT_NS_CLASS_ENTRY(ce, "pq", "Converter", php_pqconv_methods); php_pqconv_class_entry = zend_register_internal_interface(&ce TSRMLS_CC); memset(&ce, 0, sizeof(ce)); INIT_NS_CLASS_ENTRY(ce ,"pq", "DateTime", php_pqdt_methods); php_pqdt_class_entry = zend_register_internal_class_ex(&ce, php_date_get_date_ce(), "DateTime" TSRMLS_CC); zend_declare_property_stringl(php_pqdt_class_entry, ZEND_STRL("format"), ZEND_STRL("Y-m-d H:i:s.uO"), ZEND_ACC_PUBLIC TSRMLS_CC); /* stop reading this file right here! */ if (SUCCESS == zend_hash_find(CG(class_table), ZEND_STRS("jsonserializable"), (void *) &json)) { zend_class_implements(php_pqdt_class_entry TSRMLS_CC, 1, *json); } return SUCCESS; } typedef struct _HashTableList { HashTable ht; struct _HashTableList *parent; } HashTableList; typedef struct _ArrayParserState { const char *ptr, *end; HashTableList *list; php_pqres_t *res; #ifdef ZTS void ***ts; #endif Oid typ; unsigned quotes:1; unsigned escaped:1; } ArrayParserState; static char caa(ArrayParserState *a, const char *any, unsigned advance) { const char *p = any; TSRMLS_FETCH_FROM_CTX(a->ts); do { if (*p == *a->ptr) { a->ptr += advance; return *p; } } while (*++p); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse array: expected one of '%s', got '%c'", any, *a->ptr); \ return 0; } static STATUS add_element(ArrayParserState *a, const char *start) { zval *zelem; size_t el_len = a->ptr - start; char *el_str = estrndup(start, el_len); TSRMLS_FETCH_FROM_CTX(a->ts); if (a->quotes) { int tmp_len = el_len; php_stripslashes(el_str, &tmp_len TSRMLS_CC); el_len = tmp_len; } else if ((a->ptr - start == 4) && !strncmp(start, "NULL", 4)) { efree(el_str); el_str = NULL; el_len = 0; } if (!el_str) { MAKE_STD_ZVAL(zelem); ZVAL_NULL(zelem); } else { zelem = php_pqres_typed_zval(a->res, el_str, el_len, a->typ TSRMLS_CC); efree(el_str); } return zend_hash_next_index_insert(&a->list->ht, &zelem, sizeof(zval *), NULL); } static STATUS parse_array(ArrayParserState *a); static STATUS parse_element(ArrayParserState *a) { const char *el; TSRMLS_FETCH_FROM_CTX(a->ts); switch (*a->ptr) { case '{': return parse_array(a); case '"': a->quotes = 1; ++a->ptr; break; } for (el = a->ptr; a->ptr < a->end; ++a->ptr) { switch (*a->ptr) { case '"': if (a->escaped) { a->escaped = 0; } else if (a->quotes) { if (SUCCESS != add_element(a, el)) { return FAILURE; } a->quotes = 0; ++a->ptr; return SUCCESS; } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse element, unexpected quote: '%.*s'", (int) (a->ptr - el), el); return FAILURE; } break; case ',': case '}': if (!a->quotes) { return add_element(a, el); } break; case '\\': a->escaped = !a->escaped; break; default: a->escaped = 0; break; } } php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse element, reached end of input"); return FAILURE; } static STATUS parse_elements(ArrayParserState *a) { TSRMLS_FETCH_FROM_CTX(a->ts); while (SUCCESS == parse_element(a)) { switch (caa(a, ",}", 0)) { case 0: return FAILURE; case '}': return SUCCESS; default: if (!*++a->ptr) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse elements, reached end of input"); return FAILURE; } break; } } return FAILURE; } static STATUS parse_array(ArrayParserState *a) { HashTableList *list; if (!caa(a, "{", 1)) { return FAILURE; } list = ecalloc(1, sizeof(*list)); ZEND_INIT_SYMTABLE(&list->ht); if (a->list) { zval *zcur; MAKE_STD_ZVAL(zcur); Z_TYPE_P(zcur) = IS_ARRAY; Z_ARRVAL_P(zcur) = &list->ht; zend_hash_next_index_insert(&a->list->ht, &zcur, sizeof(zval *), NULL); list->parent = a->list; } a->list = list; if (SUCCESS != parse_elements(a)) { return FAILURE; } if (!caa(a, "}", 1)) { return FAILURE; } if (a->list->parent) { a->list = a->list->parent; } return SUCCESS; } HashTable *php_pq_parse_array(php_pqres_t *res, const char *val_str, size_t val_len, Oid typ TSRMLS_DC) { HashTable *ht = NULL; ArrayParserState a = {0}; TSRMLS_SET_CTX(a.ts); a.typ = typ; a.ptr = val_str; a.end = val_str + val_len; a.res = res; if (SUCCESS != parse_array(&a)) { while (a.list) { HashTableList *l = a.list->parent; zend_hash_destroy(&a.list->ht); efree(a.list); a.list = l; } return ht; } if (*a.ptr) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Trailing input: '%s'", a.ptr); } do { ht = &a.list->ht; } while ((a.list = a.list->parent)); return ht; } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifndef PHP_PQ_ERROR_H #define PHP_PQ_ERROR_H #include <libpq-fe.h> typedef int STATUS; /* SUCCESS/FAILURE */ #include "php_pqres.h" /* TSRM morony */ #if PHP_VERSION_ID >= 50700 # define z_is_true(z) zend_is_true(z TSRMLS_CC) #else # define z_is_true zend_is_true #endif /* trim LF from EOL */ extern char *php_pq_rtrim(char *e); /* R, W, RW */ extern const char *php_pq_strmode(long mode); /* compare array index */ extern int php_pq_compare_index(const void *lptr, const void *rptr TSRMLS_DC); #define PHP_PQerrorMessage(c) php_pq_rtrim(PQerrorMessage((c))) #define PHP_PQresultErrorMessage(r) php_pq_rtrim(PQresultErrorMessage((r))) extern zend_class_entry *php_pqdt_class_entry; extern zval *php_pqdt_from_string(zval *zv, char *input_fmt, char *dt_str, size_t dt_len, char *output_fmt, zval *ztimezone TSRMLS_DC); extern void php_pqdt_to_string(zval *zdt, const char *format, char **str_buf, size_t *str_len TSRMLS_DC); extern zend_class_entry *php_pqconv_class_entry; extern HashTable *php_pq_parse_array(php_pqres_t *res, const char *val_str, size_t val_len, Oid typ TSRMLS_DC); extern PHP_MINIT_FUNCTION(pq_misc); #endif /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <php.h> #include <ext/standard/info.h> #include <libpq-fe.h> #include "php_pq.h" #include "php_pq_misc.h" #include "php_pqcancel.h" #include "php_pqconn.h" #include "php_pqcopy.h" #include "php_pqcur.h" #include "php_pqexc.h" #include "php_pqlob.h" #include "php_pqres.h" #include "php_pqstm.h" #include "php_pqtxn.h" #include "php_pqtypes.h" #define PHP_MINIT_CALL(i) do { \ if (SUCCESS != PHP_MINIT(i)(type, module_number TSRMLS_CC)) { \ return FAILURE; \ } \ } while(0) static PHP_MINIT_FUNCTION(pq) { PHP_MINIT_CALL(pq_misc); PHP_MINIT_CALL(pqexc); PHP_MINIT_CALL(pqconn); PHP_MINIT_CALL(pqcancel); PHP_MINIT_CALL(pqtypes); PHP_MINIT_CALL(pqres); PHP_MINIT_CALL(pqstm); PHP_MINIT_CALL(pqtxn); PHP_MINIT_CALL(pqcur); PHP_MINIT_CALL(pqcopy); PHP_MINIT_CALL(pqlob); return php_persistent_handle_provide(ZEND_STRL("pq\\Connection"), php_pqconn_get_resource_factory_ops(), NULL, NULL TSRMLS_CC); } #define PHP_MSHUT_CALL(i) do { \ if (SUCCESS != PHP_MSHUTDOWN(i)(type, module_number TSRMLS_CC)) { \ return FAILURE; \ } \ } while(0) static PHP_MSHUTDOWN_FUNCTION(pq) { php_persistent_handle_cleanup(ZEND_STRL("pq\\Connection"), NULL, 0 TSRMLS_CC); PHP_MSHUT_CALL(pqlob); PHP_MSHUT_CALL(pqcopy); PHP_MSHUT_CALL(pqcur); PHP_MSHUT_CALL(pqtxn); PHP_MSHUT_CALL(pqstm); PHP_MSHUT_CALL(pqres); PHP_MSHUT_CALL(pqtypes); PHP_MSHUT_CALL(pqcancel); PHP_MSHUT_CALL(pqconn); return SUCCESS; } static PHP_MINFO_FUNCTION(pq) { #ifdef HAVE_PQLIBVERSION int libpq_v; #endif char libpq_version[10] = "pre-9.1"; php_info_print_table_start(); php_info_print_table_header(2, "PQ Support", "enabled"); php_info_print_table_row(2, "Extension Version", PHP_PQ_VERSION); php_info_print_table_end(); php_info_print_table_start(); php_info_print_table_header(2, "Used Library", "Version"); #ifdef HAVE_PQLIBVERSION libpq_v = PQlibVersion(); slprintf(libpq_version, sizeof(libpq_version), "%d.%d.%d", libpq_v/10000%100, libpq_v/100%100, libpq_v%100); #endif php_info_print_table_row(2, "libpq", libpq_version); php_info_print_table_end(); } static const zend_function_entry pq_functions[] = { {0} }; static zend_module_dep pq_module_deps[] = { ZEND_MOD_REQUIRED("raphf") ZEND_MOD_REQUIRED("spl") ZEND_MOD_OPTIONAL("json") ZEND_MOD_END }; zend_module_entry pq_module_entry = { STANDARD_MODULE_HEADER_EX, NULL, pq_module_deps, "pq", pq_functions, PHP_MINIT(pq), PHP_MSHUTDOWN(pq), NULL,/*PHP_RINIT(pq),*/ NULL,/*PHP_RSHUTDOWN(pq),*/ PHP_MINFO(pq), PHP_PQ_VERSION, STANDARD_MODULE_PROPERTIES }; #ifdef COMPILE_DL_PQ ZEND_GET_MODULE(pq) #endif /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <php.h> #include "php_pq_object.h" void php_pq_object_to_zval(void *o, zval **zv TSRMLS_DC) { php_pq_object_t *obj = o; if (!*zv) { MAKE_STD_ZVAL(*zv); } zend_objects_store_add_ref_by_handle(obj->zv.handle TSRMLS_CC); (*zv)->type = IS_OBJECT; (*zv)->value.obj = obj->zv; } void php_pq_object_to_zval_no_addref(void *o, zval **zv TSRMLS_DC) { php_pq_object_t *obj = o; if (!*zv) { MAKE_STD_ZVAL(*zv); } /* no add ref */ (*zv)->type = IS_OBJECT; (*zv)->value.obj = obj->zv; } void php_pq_object_addref(void *o TSRMLS_DC) { php_pq_object_t *obj = o; zend_objects_store_add_ref_by_handle(obj->zv.handle TSRMLS_CC); } void php_pq_object_delref(void *o TSRMLS_DC) { php_pq_object_t *obj = o; zend_objects_store_del_ref_by_handle_ex(obj->zv.handle, obj->zv.handlers TSRMLS_CC); } struct apply_pi_to_ht_arg { HashTable *ht; zval *object; php_pq_object_t *pq_obj; unsigned addref:1; }; static int apply_pi_to_ht(void *p, void *a TSRMLS_DC) { zend_property_info *pi = p; struct apply_pi_to_ht_arg *arg = a; zval *property = zend_read_property(arg->pq_obj->zo.ce, arg->object, pi->name, pi->name_length, 0 TSRMLS_CC); if (arg->addref) { Z_ADDREF_P(property); } zend_hash_update(arg->ht, pi->name, pi->name_length + 1, (void *) &property, sizeof(zval *), NULL); return ZEND_HASH_APPLY_KEEP; } HashTable *php_pq_object_debug_info(zval *object, int *temp TSRMLS_DC) { struct apply_pi_to_ht_arg arg = {NULL}; *temp = 1; ALLOC_HASHTABLE(arg.ht); ZEND_INIT_SYMTABLE(arg.ht); arg.object = object; arg.pq_obj = zend_object_store_get_object(object TSRMLS_CC); arg.addref = 1; zend_hash_apply_with_argument(&arg.pq_obj->zo.ce->properties_info, apply_pi_to_ht, &arg TSRMLS_CC); return arg.ht; } HashTable *php_pq_object_properties(zval *object TSRMLS_DC) { struct apply_pi_to_ht_arg arg = {NULL}; arg.ht = zend_get_std_object_handlers()->get_properties(object TSRMLS_CC); arg.object = object; arg.pq_obj = zend_object_store_get_object(object TSRMLS_CC); arg.addref = 1; zend_hash_apply_with_argument(&arg.pq_obj->zo.ce->properties_info, apply_pi_to_ht, &arg TSRMLS_CC); return arg.ht; } zend_class_entry *ancestor(zend_class_entry *ce) { while (ce->parent) { ce = ce->parent; } return ce; } zval *php_pq_object_read_prop(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC) { php_pq_object_t *obj = zend_object_store_get_object(object TSRMLS_CC); php_pq_object_prophandler_t *handler; zval *return_value = NULL; if (!obj->intern) { php_error(E_RECOVERABLE_ERROR, "%s not initialized", ancestor(obj->zo.ce)->name); return_value = zend_get_std_object_handlers()->read_property(object, member, type, key TSRMLS_CC); } else if ((SUCCESS != zend_hash_find(obj->prophandler, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, (void *) &handler)) || !handler->read) { return_value = zend_get_std_object_handlers()->read_property(object, member, type, key TSRMLS_CC); } else if (type != BP_VAR_R) { php_error(E_WARNING, "Cannot access %s properties by reference or array key/index", ancestor(obj->zo.ce)->name); return_value = zend_get_std_object_handlers()->read_property(object, member, type, key TSRMLS_CC); } else { ALLOC_ZVAL(return_value); Z_SET_REFCOUNT_P(return_value, 0); Z_UNSET_ISREF_P(return_value); handler->read(object, obj, return_value TSRMLS_CC); } return return_value; } void php_pq_object_write_prop(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC) { php_pq_object_t *obj = zend_object_store_get_object(object TSRMLS_CC); php_pq_object_prophandler_t *handler; if (!obj->intern) { php_error(E_RECOVERABLE_ERROR, "%s not initialized", ancestor(obj->zo.ce)->name); zend_get_std_object_handlers()->write_property(object, member, value, key TSRMLS_CC); } else if (SUCCESS == zend_hash_find(obj->prophandler, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, (void *) &handler)) { if (handler->write) { handler->write(object, obj, value TSRMLS_CC); } } else { zend_get_std_object_handlers()->write_property(object, member, value, key TSRMLS_CC); } } /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifndef PHP_PQ_OBJECT_H #define PHP_PQ_OBJECT_H typedef struct php_pq_object { zend_object zo; zend_object_value zv; HashTable *prophandler; void *intern; } php_pq_object_t; typedef void (*php_pq_object_prophandler_func_t)(zval *object, void *o, zval *return_value TSRMLS_DC); typedef struct php_pq_object_prophandler { php_pq_object_prophandler_func_t read; php_pq_object_prophandler_func_t write; } php_pq_object_prophandler_t; extern void php_pq_object_to_zval(void *o, zval **zv TSRMLS_DC); extern void php_pq_object_to_zval_no_addref(void *o, zval **zv TSRMLS_DC); extern void php_pq_object_addref(void *o TSRMLS_DC); extern void php_pq_object_delref(void *o TSRMLS_DC); extern HashTable *php_pq_object_debug_info(zval *object, int *temp TSRMLS_DC); extern HashTable *php_pq_object_properties(zval *object TSRMLS_DC); extern zend_class_entry *ancestor(zend_class_entry *ce); extern zval *php_pq_object_read_prop(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC); extern void php_pq_object_write_prop(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC); #endif /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <php.h> #include <ext/standard/php_string.h> #include <ext/standard/php_smart_str.h> #if PHP_PQ_HAVE_PHP_JSON_H #include <php_json.h> /* we've added the include directory to INCLUDES */ #endif #include <Zend/zend_interfaces.h> #include <libpq-fe.h> #include "php_pq.h" #include "php_pq_params.h" #include "php_pq_misc.h" #undef PHP_PQ_TYPE #include "php_pq_type.h" void php_pq_params_set_type_conv(php_pq_params_t *p, HashTable *conv) { zend_hash_clean(&p->type.conv); zend_hash_copy(&p->type.conv, conv, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); } static int apply_to_oid(void *p, void *arg TSRMLS_DC) { Oid **types = arg; zval **ztype = p; if (Z_TYPE_PP(ztype) != IS_LONG) { convert_to_long_ex(ztype); } **types = Z_LVAL_PP(ztype); ++*types; if (*ztype != *(zval **)p) { zval_ptr_dtor(ztype); } return ZEND_HASH_APPLY_KEEP; } unsigned php_pq_params_set_type_oids(php_pq_params_t *p, HashTable *oids) { p->type.count = oids ? zend_hash_num_elements(oids) : 0; TSRMLS_DF(p); if (p->type.oids) { efree(p->type.oids); p->type.oids = NULL; } if (p->type.count) { Oid *ptr = ecalloc(p->type.count + 1, sizeof(*p->type.oids)); /* +1 for when less types than params are specified */ p->type.oids = ptr; zend_hash_apply_with_argument(oids, apply_to_oid, &ptr TSRMLS_CC); } return p->type.count; } unsigned php_pq_params_add_type_oid(php_pq_params_t *p, Oid type) { p->type.oids = safe_erealloc(p->type.oids, ++p->type.count, sizeof(*p->type.oids), sizeof(*p->type.oids)); p->type.oids[p->type.count] = 0; p->type.oids[p->type.count-1] = type; return p->type.count; } static zval *object_param_to_string(php_pq_params_t *p, zval *zobj, Oid type TSRMLS_DC) { zval *return_value = NULL; smart_str str = {0}; switch (type) { #if PHP_PQ_HAVE_PHP_JSON_H && defined(PHP_PQ_OID_JSON) # ifdef PHP_PQ_OID_JSONB case PHP_PQ_OID_JSONB: # endif case PHP_PQ_OID_JSON: php_json_encode(&str, zobj, PHP_JSON_UNESCAPED_UNICODE TSRMLS_CC); smart_str_0(&str); break; #endif case PHP_PQ_OID_DATE: php_pqdt_to_string(zobj, "Y-m-d", &str.c, &str.len TSRMLS_CC); break; case PHP_PQ_OID_ABSTIME: php_pqdt_to_string(zobj, "Y-m-d H:i:s", &str.c, &str.len TSRMLS_CC); break; case PHP_PQ_OID_TIMESTAMP: php_pqdt_to_string(zobj, "Y-m-d H:i:s.u", &str.c, &str.len TSRMLS_CC); break; case PHP_PQ_OID_TIMESTAMPTZ: php_pqdt_to_string(zobj, "Y-m-d H:i:s.uO", &str.c, &str.len TSRMLS_CC); break; default: MAKE_STD_ZVAL(return_value); MAKE_COPY_ZVAL(&zobj, return_value); convert_to_string(return_value); break; } if (str.c) { MAKE_STD_ZVAL(return_value); RETVAL_STRINGL(str.c, str.len, 0); } return return_value; } struct apply_to_param_from_array_arg { php_pq_params_t *params; unsigned index; smart_str *buffer; Oid type; zval **zconv; }; static int apply_to_param_from_array(void *ptr, void *arg_ptr TSRMLS_DC) { struct apply_to_param_from_array_arg subarg, *arg = arg_ptr; zval *ztmp, **zparam = ptr, *zcopy = *zparam; char *tmp; size_t len; int tmp_len; if (arg->index++) { smart_str_appendc(arg->buffer, ','); } if (arg->zconv) { zval *ztype, *rv = NULL; MAKE_STD_ZVAL(ztype); ZVAL_LONG(ztype, arg->type); zend_call_method_with_2_params(arg->zconv, NULL, NULL, "converttostring", &rv, zcopy, ztype); zval_ptr_dtor(&ztype); if (rv) { convert_to_string(rv); zcopy = rv; } else { return ZEND_HASH_APPLY_STOP; } goto append_string; } else { switch (Z_TYPE_P(zcopy)) { case IS_NULL: smart_str_appends(arg->buffer, "NULL"); break; case IS_BOOL: smart_str_appends(arg->buffer, Z_BVAL_P(zcopy) ? "t" : "f"); break; case IS_LONG: smart_str_append_long(arg->buffer, Z_LVAL_P(zcopy)); break; case IS_DOUBLE: len = spprintf(&tmp, 0, "%F", Z_DVAL_P(zcopy)); smart_str_appendl(arg->buffer, tmp, len); efree(tmp); break; case IS_ARRAY: subarg = *arg; subarg.index = 0; smart_str_appendc(arg->buffer, '{'); zend_hash_apply_with_argument(Z_ARRVAL_P(zcopy), apply_to_param_from_array, &subarg TSRMLS_CC); smart_str_appendc(arg->buffer, '}'); break; case IS_OBJECT: if ((ztmp = object_param_to_string(arg->params, zcopy, arg->type TSRMLS_CC))) { zcopy = ztmp; } /* no break */ default: SEPARATE_ZVAL(&zcopy); convert_to_string(zcopy); append_string: tmp = php_addslashes(Z_STRVAL_P(zcopy), Z_STRLEN_P(zcopy), &tmp_len, 0 TSRMLS_CC); smart_str_appendc(arg->buffer, '"'); smart_str_appendl(arg->buffer, tmp, tmp_len); smart_str_appendc(arg->buffer, '"'); if (zcopy != *zparam) { zval_ptr_dtor(&zcopy); } efree(tmp); break; } } ++arg->index; return ZEND_HASH_APPLY_KEEP; } static zval *array_param_to_string(php_pq_params_t *p, zval *zarr, Oid type TSRMLS_DC) { zval *zcopy, *return_value; smart_str s = {0}; struct apply_to_param_from_array_arg arg = {NULL}; switch (type) { #if PHP_PQ_HAVE_PHP_JSON_H && defined(PHP_PQ_OID_JSON) # ifdef PHP_PQ_OID_JSONB case PHP_PQ_OID_JSONB: # endif case PHP_PQ_OID_JSON: php_json_encode(&s, zarr, PHP_JSON_UNESCAPED_UNICODE TSRMLS_CC); smart_str_0(&s); break; #endif default: arg.params = p; arg.buffer = &s; arg.type = PHP_PQ_TYPE_OF_ARRAY(type); zend_hash_index_find(&p->type.conv, PHP_PQ_TYPE_OF_ARRAY(type), (void *) &arg.zconv); smart_str_appendc(arg.buffer, '{'); MAKE_STD_ZVAL(zcopy); MAKE_COPY_ZVAL(&zarr, zcopy); zend_hash_apply_with_argument(Z_ARRVAL_P(zcopy), apply_to_param_from_array, &arg TSRMLS_CC); zval_ptr_dtor(&zcopy); smart_str_appendc(arg.buffer, '}'); smart_str_0(&s); break; } /* must not return NULL */ MAKE_STD_ZVAL(return_value); if (s.c) { RETVAL_STRINGL(s.c, s.len, 0); } else { RETVAL_EMPTY_STRING(); } return return_value; } static void php_pq_params_set_param(php_pq_params_t *p, unsigned index, zval **zpp) { zval **zconv = NULL; Oid type = p->type.count > index ? p->type.oids[index] : 0; TSRMLS_DF(p); if (type && SUCCESS == zend_hash_index_find(&p->type.conv, type, (void *) &zconv)) { zval *ztype, *rv = NULL; MAKE_STD_ZVAL(ztype); ZVAL_LONG(ztype, type); zend_call_method_with_2_params(zconv, NULL, NULL, "converttostring", &rv, *zpp, ztype); zval_ptr_dtor(&ztype); if (rv) { convert_to_string(rv); p->param.strings[index] = Z_STRVAL_P(rv); zend_hash_next_index_insert(&p->param.dtor, (void *) &rv, sizeof(zval *), NULL); } } else { zval *tmp, *zcopy = *zpp; switch (Z_TYPE_P(zcopy)) { case IS_NULL: p->param.strings[index] = NULL; return; case IS_BOOL: p->param.strings[index] = Z_BVAL_P(zcopy) ? "t" : "f"; return; case IS_DOUBLE: MAKE_STD_ZVAL(zcopy); MAKE_COPY_ZVAL(zpp, zcopy); Z_TYPE_P(zcopy) = IS_STRING; Z_STRLEN_P(zcopy) = spprintf(&Z_STRVAL_P(zcopy), 0, "%F", Z_DVAL_PP(zpp)); break; case IS_ARRAY: MAKE_STD_ZVAL(zcopy); MAKE_COPY_ZVAL(zpp, zcopy); tmp = array_param_to_string(p, zcopy, type TSRMLS_CC); zval_ptr_dtor(&zcopy); zcopy = tmp; break; case IS_OBJECT: if ((tmp = object_param_to_string(p, zcopy, type TSRMLS_CC))) { zcopy = tmp; break; } /* no break */ default: convert_to_string_ex(&zcopy); break; } p->param.strings[index] = Z_STRVAL_P(zcopy); if (zcopy != *zpp) { zend_hash_next_index_insert(&p->param.dtor, (void *) &zcopy, sizeof(zval *), NULL); } } } struct apply_to_params_arg { php_pq_params_t *params; unsigned index; }; static int apply_to_params(void *zp, void *arg_ptr TSRMLS_DC) { struct apply_to_params_arg *arg = arg_ptr; SEPARATE_ZVAL_IF_NOT_REF((zval **) zp); php_pq_params_set_param(arg->params, arg->index++, zp); return ZEND_HASH_APPLY_KEEP; } unsigned php_pq_params_add_param(php_pq_params_t *p, zval *param) { p->param.strings = safe_erealloc(p->param.strings, ++p->param.count, sizeof(*p->param.strings), 0); php_pq_params_set_param(p, p->param.count-1, ¶m); return p->type.count; } unsigned php_pq_params_set_params(php_pq_params_t *p, HashTable *params) { p->param.count = params ? zend_hash_num_elements(params) : 0; TSRMLS_DF(p); if (p->param.strings) { efree(p->param.strings); p->param.strings = NULL; } zend_hash_clean(&p->param.dtor); if (p->param.count) { struct apply_to_params_arg arg = {p, 0}; p->param.strings = ecalloc(p->param.count, sizeof(*p->param.strings)); zend_hash_apply_with_argument(params, apply_to_params, &arg TSRMLS_CC); } return p->param.count; } void php_pq_params_free(php_pq_params_t **p) { if (*p) { php_pq_params_set_type_oids(*p, NULL); php_pq_params_set_params(*p, NULL); zend_hash_destroy(&(*p)->param.dtor); zend_hash_destroy(&(*p)->type.conv); efree(*p); *p = NULL; } } php_pq_params_t *php_pq_params_init(HashTable *conv, HashTable *oids, HashTable *params TSRMLS_DC) { php_pq_params_t *p = ecalloc(1, sizeof(*p)); TSRMLS_CF(p); zend_hash_init(&p->type.conv, 0, NULL, ZVAL_PTR_DTOR, 0); zend_hash_init(&p->param.dtor, 0, NULL, ZVAL_PTR_DTOR, 0); if (conv) { php_pq_params_set_type_conv(p, conv); } if (oids) { php_pq_params_set_type_oids(p, oids); } if (params) { php_pq_params_set_params(p, params); } return p; } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifndef PHP_PQ_PARAMS_H #define PHP_PQ_PARAMS_H typedef struct php_pq_params { struct { HashTable conv; unsigned count; Oid *oids; } type; struct { HashTable dtor; unsigned count; char **strings; } param; #ifdef ZTS void ***ts; #endif } php_pq_params_t; extern php_pq_params_t *php_pq_params_init(HashTable *conv, HashTable *oids, HashTable *params TSRMLS_DC); extern void php_pq_params_free(php_pq_params_t **p); extern unsigned php_pq_params_set_params(php_pq_params_t *p, HashTable *params); extern unsigned php_pq_params_set_type_oids(php_pq_params_t *p, HashTable *oids); extern unsigned php_pq_params_add_type_oid(php_pq_params_t *p, Oid type); extern unsigned php_pq_params_add_param(php_pq_params_t *p, zval *param); extern void php_pq_params_set_type_conv(php_pq_params_t *p, HashTable *conv); #endif /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <php.h> #include <ext/spl/spl_iterators.h> #if PHP_PQ_HAVE_PHP_JSON_H #include <php_json.h> /* we've added the include directory to INCLUDES */ #endif #include <libpq-events.h> #include "php_pq.h" #include "php_pq_misc.h" #include "php_pq_object.h" #include "php_pqexc.h" #include "php_pqres.h" #undef PHP_PQ_TYPE #include "php_pq_type.h" zend_class_entry *php_pqres_class_entry; static zend_object_handlers php_pqres_object_handlers; static HashTable php_pqres_object_prophandlers; static zend_object_iterator_funcs php_pqres_iterator_funcs; static zend_object_iterator *php_pqres_iterator_init(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC) { php_pqres_iterator_t *iter; zval *prop, *zfetch_type; iter = ecalloc(1, sizeof(*iter)); iter->zi.funcs = &php_pqres_iterator_funcs; iter->zi.data = zend_object_store_get_object(object TSRMLS_CC); zend_objects_store_add_ref(object TSRMLS_CC); zfetch_type = prop = zend_read_property(ce, object, ZEND_STRL("fetchType"), 0 TSRMLS_CC); if (Z_TYPE_P(zfetch_type) != IS_LONG) { convert_to_long_ex(&zfetch_type); } iter->fetch_type = Z_LVAL_P(zfetch_type); if (zfetch_type != prop) { zval_ptr_dtor(&zfetch_type); } if (Z_REFCOUNT_P(prop)) { zval_ptr_dtor(&prop); } else { zval_dtor(prop); FREE_ZVAL(prop); } return (zend_object_iterator *) iter; } static void php_pqres_iterator_dtor(zend_object_iterator *i TSRMLS_DC) { php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i; php_pqres_object_t *obj = i->data; if (iter->current_val) { zval_ptr_dtor(&iter->current_val); iter->current_val = NULL; } zend_objects_store_del_ref_by_handle_ex(obj->zv.handle, obj->zv.handlers TSRMLS_CC); efree(iter); } static STATUS php_pqres_iterator_valid(zend_object_iterator *i TSRMLS_DC) { php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i; php_pqres_object_t *obj = i->data; switch (PQresultStatus(obj->intern->res)) { case PGRES_TUPLES_OK: #ifdef HAVE_PGRES_SINGLE_TUPLE case PGRES_SINGLE_TUPLE: #endif if (PQntuples(obj->intern->res) <= iter->index) { return FAILURE; } break; default: return FAILURE; } return SUCCESS; } #define PHP_PQRES_JSON_OPTIONS(res) \ (php_pqres_fetch_type(res) != PHP_PQRES_FETCH_OBJECT ? PHP_JSON_OBJECT_AS_ARRAY:0) zval *php_pqres_typed_zval(php_pqres_t *res, char *val, size_t len, Oid typ TSRMLS_DC) { zval *zv, **zconv; MAKE_STD_ZVAL(zv); if (SUCCESS == zend_hash_index_find(&res->converters, typ, (void *) &zconv)) { zval *ztype, *tmp = NULL; MAKE_STD_ZVAL(ztype); ZVAL_LONG(ztype, typ); ZVAL_STRINGL(zv, val, len, 1); zend_call_method_with_2_params(zconv, NULL, NULL, "convertfromstring", &tmp, zv, ztype); zval_ptr_dtor(&ztype); if (tmp) { zval_ptr_dtor(&zv); zv = tmp; } return zv; } switch (typ) { case PHP_PQ_OID_BOOL: if (!(res->auto_convert & PHP_PQRES_CONV_BOOL)) { goto noconversion; } ZVAL_BOOL(zv, *val == 't'); break; case PHP_PQ_OID_INT8: case PHP_PQ_OID_TID: case PHP_PQ_OID_INT4: case PHP_PQ_OID_INT2: case PHP_PQ_OID_XID: case PHP_PQ_OID_OID: if (!(res->auto_convert & PHP_PQRES_CONV_INT)) { goto noconversion; } { long lval; double dval; switch (is_numeric_string(val, len, &lval, &dval, 0)) { case IS_LONG: ZVAL_LONG(zv, lval); break; case IS_DOUBLE: ZVAL_DOUBLE(zv, dval); break; default: goto noconversion; } } break; case PHP_PQ_OID_FLOAT4: case PHP_PQ_OID_FLOAT8: if (!(res->auto_convert & PHP_PQRES_CONV_FLOAT)) { goto noconversion; } ZVAL_DOUBLE(zv, zend_strtod(val, NULL)); break; case PHP_PQ_OID_DATE: if (!(res->auto_convert & PHP_PQRES_CONV_DATETIME)) { goto noconversion; } php_pqdt_from_string(zv, NULL, val, len, "Y-m-d", NULL TSRMLS_CC); break; case PHP_PQ_OID_ABSTIME: if (!(res->auto_convert & PHP_PQRES_CONV_DATETIME)) { goto noconversion; } php_pqdt_from_string(zv, NULL, val, len, "Y-m-d H:i:s", NULL TSRMLS_CC); break; case PHP_PQ_OID_TIMESTAMP: if (!(res->auto_convert & PHP_PQRES_CONV_DATETIME)) { goto noconversion; } php_pqdt_from_string(zv, NULL, val, len, "Y-m-d H:i:s.u", NULL TSRMLS_CC); break; case PHP_PQ_OID_TIMESTAMPTZ: if (!(res->auto_convert & PHP_PQRES_CONV_DATETIME)) { goto noconversion; } php_pqdt_from_string(zv, NULL, val, len, "Y-m-d H:i:s.uO", NULL TSRMLS_CC); break; #if PHP_PQ_HAVE_PHP_JSON_H && defined(PHP_PQ_OID_JSON) # ifdef PHP_PQ_OID_JSONB case PHP_PQ_OID_JSONB: # endif case PHP_PQ_OID_JSON: if (!(res->auto_convert & PHP_PQRES_CONV_JSON)) { goto noconversion; } php_json_decode_ex(zv, val, len, PHP_PQRES_JSON_OPTIONS(res), 512 /* PHP_JSON_DEFAULT_DEPTH */ TSRMLS_CC); break; #endif default: if (!(res->auto_convert & PHP_PQRES_CONV_ARRAY)) { goto noconversion; } if (PHP_PQ_TYPE_IS_ARRAY(typ) && (Z_ARRVAL_P(zv) = php_pq_parse_array(res, val, len, PHP_PQ_TYPE_OF_ARRAY(typ) TSRMLS_CC))) { Z_TYPE_P(zv) = IS_ARRAY; } else { noconversion: ZVAL_STRINGL(zv, val, len, 1); } break; } return zv; } static inline zval *php_pqres_get_col(php_pqres_t *r, unsigned row, unsigned col TSRMLS_DC) { zval *zv; if (PQgetisnull(r->res, row, col)) { MAKE_STD_ZVAL(zv); ZVAL_NULL(zv); } else { zv = php_pqres_typed_zval(r, PQgetvalue(r->res, row, col), PQgetlength(r->res, row, col), PQftype(r->res, col) TSRMLS_CC); } return zv; } static inline void php_pqres_add_col_to_zval(php_pqres_t *r, unsigned row, unsigned col, php_pqres_fetch_t fetch_type, zval *data TSRMLS_DC) { if (PQgetisnull(r->res, row, col)) { switch (fetch_type) { case PHP_PQRES_FETCH_OBJECT: add_property_null(data, PQfname(r->res, col)); break; case PHP_PQRES_FETCH_ASSOC: add_assoc_null(data, PQfname(r->res, col)); break; case PHP_PQRES_FETCH_ARRAY: add_index_null(data, col); break; } } else { zval *zv; zv = php_pqres_typed_zval(r, PQgetvalue(r->res, row, col), PQgetlength(r->res, row, col), PQftype(r->res, col) TSRMLS_CC); switch (fetch_type) { case PHP_PQRES_FETCH_OBJECT: add_property_zval(data, PQfname(r->res, col), zv); zval_ptr_dtor(&zv); break; case PHP_PQRES_FETCH_ASSOC: add_assoc_zval(data, PQfname(r->res, col), zv); break; case PHP_PQRES_FETCH_ARRAY: add_index_zval(data, col, zv); break; } } } zval *php_pqres_row_to_zval(PGresult *res, unsigned row, php_pqres_fetch_t fetch_type, zval **data_ptr TSRMLS_DC) { zval *data = NULL; int c, cols; php_pqres_object_t *res_obj = PQresultInstanceData(res, php_pqconn_event); if (data_ptr) { data = *data_ptr; } if (!data) { MAKE_STD_ZVAL(data); if (PHP_PQRES_FETCH_OBJECT == fetch_type) { object_init(data); } else { array_init(data); } if (data_ptr) { *data_ptr = data; } } if (PQntuples(res) > row) { for (c = 0, cols = PQnfields(res); c < cols; ++c) { php_pqres_add_col_to_zval(res_obj->intern, row, c, fetch_type, data TSRMLS_CC); } } return data; } static void php_pqres_iterator_current(zend_object_iterator *i, zval ***data_ptr TSRMLS_DC) { php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i; php_pqres_object_t *obj = i->data; if (!iter->current_val) { iter->current_val = php_pqres_row_to_zval(obj->intern->res, iter->index, iter->fetch_type, NULL TSRMLS_CC); } *data_ptr = &iter->current_val; } #if PHP_VERSION_ID >= 50500 static void php_pqres_iterator_key(zend_object_iterator *i, zval *key TSRMLS_DC) { php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i; ZVAL_LONG(key, iter->index); } #else static int php_pqres_iterator_key(zend_object_iterator *i, char **key_str, uint *key_len, ulong *key_num TSRMLS_DC) { php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i; *key_num = (ulong) iter->index; return HASH_KEY_IS_LONG; } #endif static void php_pqres_iterator_invalidate(zend_object_iterator *i TSRMLS_DC) { php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i; if (iter->current_val) { zval_ptr_dtor(&iter->current_val); iter->current_val = NULL; } } static void php_pqres_iterator_next(zend_object_iterator *i TSRMLS_DC) { php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i; php_pqres_iterator_invalidate(i TSRMLS_CC); ++iter->index; } static void php_pqres_iterator_rewind(zend_object_iterator *i TSRMLS_DC) { php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i; php_pqres_iterator_invalidate(i TSRMLS_CC); iter->index = 0; } static zend_object_iterator_funcs php_pqres_iterator_funcs = { php_pqres_iterator_dtor, /* check for end of iteration (FAILURE or SUCCESS if data is valid) */ php_pqres_iterator_valid, /* fetch the item data for the current element */ php_pqres_iterator_current, /* fetch the key for the current element (return HASH_KEY_IS_STRING or HASH_KEY_IS_LONG) (optional, may be NULL) */ php_pqres_iterator_key, /* step forwards to next element */ php_pqres_iterator_next, /* rewind to start of data (optional, may be NULL) */ php_pqres_iterator_rewind, /* invalidate current value/key (optional, may be NULL) */ php_pqres_iterator_invalidate }; static int php_pqres_count_elements(zval *object, long *count TSRMLS_DC) { php_pqres_object_t *obj = zend_object_store_get_object(object TSRMLS_CC); if (!obj->intern) { return FAILURE; } else { *count = (long) PQntuples(obj->intern->res); return SUCCESS; } } STATUS php_pqres_success(PGresult *res TSRMLS_DC) { zval *zexc; switch (PQresultStatus(res)) { case PGRES_BAD_RESPONSE: case PGRES_NONFATAL_ERROR: case PGRES_FATAL_ERROR: zexc = throw_exce(EX_SQL TSRMLS_CC, "%s", PHP_PQresultErrorMessage(res)); zend_update_property_string(Z_OBJCE_P(zexc), zexc, ZEND_STRL("sqlstate"), PQresultErrorField(res, PG_DIAG_SQLSTATE) TSRMLS_CC); return FAILURE; default: return SUCCESS; } } void php_pqres_init_instance_data(PGresult *res, php_pqconn_object_t *conn_obj, php_pqres_object_t **ptr TSRMLS_DC) { php_pqres_object_t *obj; php_pqres_t *r = ecalloc(1, sizeof(*r)); r->res = res; zend_hash_init(&r->bound, 0, 0, ZVAL_PTR_DTOR, 0); zend_hash_init(&r->converters, 0, 0, ZVAL_PTR_DTOR, 0); zend_hash_copy(&r->converters, &conn_obj->intern->converters, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); r->auto_convert = conn_obj->intern->default_auto_convert; r->default_fetch_type = conn_obj->intern->default_fetch_type; php_pqres_create_object_ex(php_pqres_class_entry, r, &obj TSRMLS_CC); PQresultSetInstanceData(res, php_pqconn_event, obj); if (ptr) { *ptr = obj; } } php_pqres_fetch_t php_pqres_fetch_type(php_pqres_t *res) { return res->iter ? res->iter->fetch_type : res->default_fetch_type; } static void php_pqres_object_free(void *o TSRMLS_DC) { php_pqres_object_t *obj = o; #if DBG_GC fprintf(stderr, "FREE res(#%d) %p\n", obj->zv.handle, obj); #endif if (obj->intern) { if (obj->intern->res) { PQresultSetInstanceData(obj->intern->res, php_pqconn_event, NULL); PQclear(obj->intern->res); obj->intern->res = NULL; } if (obj->intern->iter) { php_pqres_iterator_dtor((zend_object_iterator *) obj->intern->iter TSRMLS_CC); obj->intern->iter = NULL; } zend_hash_destroy(&obj->intern->bound); zend_hash_destroy(&obj->intern->converters); efree(obj->intern); obj->intern = NULL; } zend_object_std_dtor((zend_object *) o TSRMLS_CC); efree(obj); } zend_object_value php_pqres_create_object_ex(zend_class_entry *ce, php_pqres_t *intern, php_pqres_object_t **ptr TSRMLS_DC) { php_pqres_object_t *o; o = ecalloc(1, sizeof(*o)); zend_object_std_init((zend_object *) o, ce TSRMLS_CC); object_properties_init((zend_object *) o, ce); o->prophandler = &php_pqres_object_prophandlers; if (ptr) { *ptr = o; } if (intern) { o->intern = intern; } o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqres_object_free, NULL TSRMLS_CC); o->zv.handlers = &php_pqres_object_handlers; return o->zv; } static zend_object_value php_pqres_create_object(zend_class_entry *class_type TSRMLS_DC) { return php_pqres_create_object_ex(class_type, NULL, NULL TSRMLS_CC); } static void php_pqres_object_read_status(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqres_object_t *obj = o; RETVAL_LONG(PQresultStatus(obj->intern->res)); } static void php_pqres_object_read_status_message(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqres_object_t *obj = o; RETVAL_STRING(PQresStatus(PQresultStatus(obj->intern->res))+sizeof("PGRES"), 1); } static void php_pqres_object_read_error_message(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqres_object_t *obj = o; char *error = PHP_PQresultErrorMessage(obj->intern->res); if (error) { RETVAL_STRING(error, 1); } else { RETVAL_NULL(); } } static void php_pqres_object_read_num_rows(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqres_object_t *obj = o; RETVAL_LONG(PQntuples(obj->intern->res)); } static void php_pqres_object_read_num_cols(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqres_object_t *obj = o; RETVAL_LONG(PQnfields(obj->intern->res)); } static void php_pqres_object_read_affected_rows(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqres_object_t *obj = o; RETVAL_LONG(atoi(PQcmdTuples(obj->intern->res))); } static void php_pqres_object_read_fetch_type(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqres_object_t *obj = o; RETVAL_LONG(php_pqres_fetch_type(obj->intern)); } static void php_pqres_object_write_fetch_type(zval *object, void *o, zval *value TSRMLS_DC) { php_pqres_object_t *obj = o; zval *zfetch_type = value; if (Z_TYPE_P(value) != IS_LONG) { if (Z_REFCOUNT_P(value) > 1) { zval *tmp; MAKE_STD_ZVAL(tmp); ZVAL_ZVAL(tmp, zfetch_type, 1, 0); convert_to_long(tmp); zfetch_type = tmp; } else { convert_to_long_ex(&zfetch_type); } } if (!obj->intern->iter) { obj->intern->iter = (php_pqres_iterator_t *) php_pqres_iterator_init(Z_OBJCE_P(object), object, 0 TSRMLS_CC); obj->intern->iter->zi.funcs->rewind((zend_object_iterator *) obj->intern->iter TSRMLS_CC); } obj->intern->iter->fetch_type = Z_LVAL_P(zfetch_type); if (zfetch_type != value) { zval_ptr_dtor(&zfetch_type); } } static void php_pqres_object_read_auto_conv(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqres_object_t *obj = o; RETVAL_LONG(obj->intern->auto_convert); } static void php_pqres_object_write_auto_conv(zval *object, void *o, zval *value TSRMLS_DC) { php_pqres_object_t *obj = o; zval *zauto_conv = value; if (Z_TYPE_P(value) != IS_LONG) { if (Z_REFCOUNT_P(value) > 1) { zval *tmp; MAKE_STD_ZVAL(tmp); ZVAL_ZVAL(tmp, zauto_conv, 1, 0); convert_to_long(tmp); zauto_conv = tmp; } else { convert_to_long_ex(&zauto_conv); } } obj->intern->auto_convert = Z_LVAL_P(zauto_conv); if (zauto_conv != value) { zval_ptr_dtor(&zauto_conv); } } static STATUS php_pqres_iteration(zval *this_ptr, php_pqres_object_t *obj, php_pqres_fetch_t fetch_type, zval ***row TSRMLS_DC) { STATUS rv; php_pqres_fetch_t orig_fetch; if (!obj) { obj = zend_object_store_get_object(getThis() TSRMLS_CC); } if (obj->intern->iter) { obj->intern->iter->zi.funcs->move_forward((zend_object_iterator *) obj->intern->iter TSRMLS_CC); } else { obj->intern->iter = (php_pqres_iterator_t *) php_pqres_iterator_init(Z_OBJCE_P(getThis()), getThis(), 0 TSRMLS_CC); obj->intern->iter->zi.funcs->rewind((zend_object_iterator *) obj->intern->iter TSRMLS_CC); } orig_fetch = obj->intern->iter->fetch_type; obj->intern->iter->fetch_type = fetch_type; if (SUCCESS == (rv = obj->intern->iter->zi.funcs->valid((zend_object_iterator *) obj->intern->iter TSRMLS_CC))) { obj->intern->iter->zi.funcs->get_current_data((zend_object_iterator *) obj->intern->iter, row TSRMLS_CC); } obj->intern->iter->fetch_type = orig_fetch; return rv; } typedef struct php_pqres_col { char *name; int num; } php_pqres_col_t; static STATUS column_nn(php_pqres_object_t *obj, zval *zcol, php_pqres_col_t *col TSRMLS_DC) { long index = -1; char *name = NULL; if (!zcol) { index = 0; } else { switch (Z_TYPE_P(zcol)) { case IS_NULL: index = 0; break; case IS_LONG: index = Z_LVAL_P(zcol); break; default: convert_to_string(zcol); /* no break */ case IS_STRING: if (!is_numeric_string(Z_STRVAL_P(zcol), Z_STRLEN_P(zcol), &index, NULL, 0)) { name = Z_STRVAL_P(zcol); } break; } } if (name) { col->name = name; col->num = PQfnumber(obj->intern->res, name); } else { col->name = PQfname(obj->intern->res, index); col->num = index; } if (!col->name) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to find column at index %ld", index); return FAILURE; } if (col->num == -1) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to find column with name '%s'", name); return FAILURE; } return SUCCESS; } ZEND_BEGIN_ARG_INFO_EX(ai_pqres_bind, 0, 0, 2) ZEND_ARG_INFO(0, col) ZEND_ARG_INFO(1, ref) ZEND_END_ARG_INFO(); static PHP_METHOD(pqres, bind) { zval *zcol, *zref; zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z", &zcol, &zref); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Result not initialized"); } else { php_pqres_col_t col; if (SUCCESS != column_nn(obj, zcol, &col TSRMLS_CC)) { RETVAL_FALSE; } else { Z_ADDREF_P(zref); if (SUCCESS != zend_hash_index_update(&obj->intern->bound, col.num, (void *) &zref, sizeof(zval *), NULL)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to bind column %s@%d", col.name, col.num); RETVAL_FALSE; } else { zend_hash_sort(&obj->intern->bound, zend_qsort, php_pq_compare_index, 0 TSRMLS_CC); RETVAL_TRUE; } } } } } static int apply_bound(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) { zval **zvalue, **zbound = p; zval **zrow = va_arg(argv, zval **); STATUS *rv = va_arg(argv, STATUS *); if (SUCCESS != zend_hash_index_find(Z_ARRVAL_PP(zrow), key->h, (void *) &zvalue)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to find column ad index %lu", key->h); *rv = FAILURE; return ZEND_HASH_APPLY_STOP; } else { zval_dtor(*zbound); ZVAL_COPY_VALUE(*zbound, *zvalue); ZVAL_NULL(*zvalue); zval_ptr_dtor(zvalue); Z_ADDREF_P(*zbound); *zvalue = *zbound; *rv = SUCCESS; return ZEND_HASH_APPLY_KEEP; } } ZEND_BEGIN_ARG_INFO_EX(ai_pqres_fetch_bound, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqres, fetchBound) { zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters_none(); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Result not initialized"); } else { zval **row = NULL; if (SUCCESS == php_pqres_iteration(getThis(), obj, PHP_PQRES_FETCH_ARRAY, &row TSRMLS_CC) && row) { zend_replace_error_handling(EH_THROW, exce(EX_RUNTIME), &zeh TSRMLS_CC); zend_hash_apply_with_arguments(&obj->intern->bound TSRMLS_CC, apply_bound, 2, row, &rv); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS != rv) { zval_ptr_dtor(row); } else { RETVAL_ZVAL(*row, 1, 0); } } } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqres_fetch_row, 0, 0, 0) ZEND_ARG_INFO(0, fetch_type) ZEND_END_ARG_INFO(); static PHP_METHOD(pqres, fetchRow) { zend_error_handling zeh; php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); long fetch_type = -1; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &fetch_type); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Result not initialized"); } else { zval **row = NULL; if (fetch_type == -1) { fetch_type = php_pqres_fetch_type(obj->intern); } zend_replace_error_handling(EH_THROW, exce(EX_RUNTIME), &zeh TSRMLS_CC); php_pqres_iteration(getThis(), obj, fetch_type, &row TSRMLS_CC); zend_restore_error_handling(&zeh TSRMLS_CC); if (row) { RETVAL_ZVAL(*row, 1, 0); } } } } static zval **column_at(zval *row, int col TSRMLS_DC) { zval **data = NULL; HashTable *ht = HASH_OF(row); int count = zend_hash_num_elements(ht); if (col >= count) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Column index %d exceeds column count %d", col, count); } else { zend_hash_internal_pointer_reset(ht); while (col-- > 0) { zend_hash_move_forward(ht); } zend_hash_get_current_data(ht, (void *) &data); } return data; } ZEND_BEGIN_ARG_INFO_EX(ai_pqres_fetch_col, 0, 0, 1) ZEND_ARG_INFO(1, ref) ZEND_ARG_INFO(0, col) ZEND_END_ARG_INFO(); static PHP_METHOD(pqres, fetchCol) { zend_error_handling zeh; zval *zcol = NULL, *zref; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|z/!", &zref, &zcol); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Result not initialized"); } else { zval **row = NULL; zend_replace_error_handling(EH_THROW, exce(EX_RUNTIME), &zeh TSRMLS_CC); php_pqres_iteration(getThis(), obj, php_pqres_fetch_type(obj->intern), &row TSRMLS_CC); if (row) { php_pqres_col_t col; if (SUCCESS != column_nn(obj, zcol, &col TSRMLS_CC)) { RETVAL_FALSE; } else { zval **zres = column_at(*row, col.num TSRMLS_CC); if (!zres) { RETVAL_FALSE; } else { zval_dtor(zref); ZVAL_ZVAL(zref, *zres, 1, 0); RETVAL_TRUE; } } } zend_restore_error_handling(&zeh TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqres_fetch_all_cols, 0, 0, 0) ZEND_ARG_INFO(0, col) ZEND_END_ARG_INFO(); static PHP_METHOD(pqres, fetchAllCols) { zend_error_handling zeh; zval *zcol = NULL; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!", &zcol); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Result not initialized"); } else { php_pqres_col_t col; zend_replace_error_handling(EH_THROW, exce(EX_RUNTIME), &zeh TSRMLS_CC); if (SUCCESS == column_nn(obj, zcol, &col TSRMLS_CC)) { int r, rows = PQntuples(obj->intern->res); array_init(return_value); for (r = 0; r < rows; ++r) { add_next_index_zval(return_value, php_pqres_get_col(obj->intern, r, col.num TSRMLS_CC)); } } zend_restore_error_handling(&zeh TSRMLS_CC); } } } struct apply_to_col_arg { php_pqres_object_t *obj; php_pqres_col_t *cols; STATUS status; }; static int apply_to_col(void *p, void *a TSRMLS_DC) { zval **c = p; struct apply_to_col_arg *arg = a; if (SUCCESS != column_nn(arg->obj, *c, arg->cols TSRMLS_CC)) { arg->status = FAILURE; return ZEND_HASH_APPLY_STOP; } else { arg->status = SUCCESS; ++arg->cols; return ZEND_HASH_APPLY_KEEP; } } static php_pqres_col_t *php_pqres_convert_to_cols(php_pqres_object_t *obj, HashTable *ht TSRMLS_DC) { struct apply_to_col_arg arg = {NULL}; php_pqres_col_t *tmp; arg.obj = obj; arg.cols = ecalloc(zend_hash_num_elements(ht), sizeof(*tmp)); tmp = arg.cols; zend_hash_apply_with_argument(ht, apply_to_col, &arg TSRMLS_CC); if (SUCCESS == arg.status) { return tmp; } else { efree(tmp); return NULL; } } ZEND_BEGIN_ARG_INFO_EX(ai_pqres_map, 0, 0, 0) ZEND_ARG_INFO(0, keys) ZEND_ARG_INFO(0, vals) ZEND_ARG_INFO(0, fetch_type) ZEND_END_ARG_INFO(); static PHP_METHOD(pqres, map) { zend_error_handling zeh; zval *zkeys = 0, *zvals = 0; long fetch_type = -1; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z/!z/!l", &zkeys, &zvals, &fetch_type); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Result not initialized"); } else { int ks = 0, vs = 0; php_pqres_col_t def = {PQfname(obj->intern->res, 0), 0}, *keys = NULL, *vals = NULL; if (zkeys) { convert_to_array(zkeys); if ((ks = zend_hash_num_elements(Z_ARRVAL_P(zkeys)))) { keys = php_pqres_convert_to_cols(obj, Z_ARRVAL_P(zkeys) TSRMLS_CC); } else { ks = 1; keys = &def; } } else { ks = 1; keys = &def; } if (zvals) { convert_to_array(zvals); if ((vs = zend_hash_num_elements(Z_ARRVAL_P(zvals)))) { vals = php_pqres_convert_to_cols(obj, Z_ARRVAL_P(zvals) TSRMLS_CC); } } if (fetch_type == -1) { fetch_type = php_pqres_fetch_type(obj->intern); } if (keys) { int rows, r; zval **cur; switch (fetch_type) { case PHP_PQRES_FETCH_ARRAY: case PHP_PQRES_FETCH_ASSOC: array_init(return_value); break; case PHP_PQRES_FETCH_OBJECT: object_init(return_value); break; } for (r = 0, rows = PQntuples(obj->intern->res); r < rows; ++r) { int k, v; cur = &return_value; for (k = 0; k < ks; ++k) { char *key = PQgetvalue(obj->intern->res, r, keys[k].num); int len = PQgetlength(obj->intern->res, r, keys[k].num); if (SUCCESS != zend_symtable_find(HASH_OF(*cur), key, len + 1, (void *) &cur)) { zval *tmp; MAKE_STD_ZVAL(tmp); switch (fetch_type) { case PHP_PQRES_FETCH_ARRAY: case PHP_PQRES_FETCH_ASSOC: array_init(tmp); break; case PHP_PQRES_FETCH_OBJECT: object_init(tmp); break; } if (SUCCESS != zend_symtable_update(HASH_OF(*cur), key, len + 1, (void *) &tmp, sizeof(zval *), (void *) &cur)) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to create map"); goto err; } } } if (vals && vs) { for (v = 0; v < vs; ++v) { char *val = PQgetvalue(obj->intern->res, r, vals[v].num); int len = PQgetlength(obj->intern->res, r, vals[v].num); switch (fetch_type) { case PHP_PQRES_FETCH_ARRAY: add_index_stringl(*cur, vals[v].num, val, len, 1); break; case PHP_PQRES_FETCH_ASSOC: add_assoc_stringl(*cur, vals[v].name, val, len, 1); break; case PHP_PQRES_FETCH_OBJECT: add_property_stringl(*cur, vals[v].name, val, len, 1); break; } } } else { php_pqres_row_to_zval(obj->intern->res, r, fetch_type, cur TSRMLS_CC); } } } err: if (keys && keys != &def) { efree(keys); } if (vals) { efree(vals); } } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqres_fetch_all, 0, 0, 0) ZEND_ARG_INFO(0, fetch_type) ZEND_END_ARG_INFO(); static PHP_METHOD(pqres, fetchAll) { zend_error_handling zeh; long fetch_type = -1; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &fetch_type); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Result not initialized"); } else { int r, rows = PQntuples(obj->intern->res); if (fetch_type == -1) { fetch_type = php_pqres_fetch_type(obj->intern); } array_init(return_value); for (r = 0; r < rows; ++r) { add_next_index_zval(return_value, php_pqres_row_to_zval(obj->intern->res, r, fetch_type, NULL TSRMLS_CC)); } } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqres_count, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqres, count) { zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters_none(); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { long count; if (SUCCESS != php_pqres_count_elements(getThis(), &count TSRMLS_CC)) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Result not initialized"); } else { RETVAL_LONG(count); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqres_desc, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqres, desc) { zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters_none(); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Result not initialized"); } else { int p, params; array_init(return_value); for (p = 0, params = PQnparams(obj->intern->res); p < params; ++p) { add_next_index_long(return_value, PQparamtype(obj->intern->res, p)); } } } } static zend_function_entry php_pqres_methods[] = { PHP_ME(pqres, bind, ai_pqres_bind, ZEND_ACC_PUBLIC) PHP_ME(pqres, fetchBound, ai_pqres_fetch_bound, ZEND_ACC_PUBLIC) PHP_ME(pqres, fetchRow, ai_pqres_fetch_row, ZEND_ACC_PUBLIC) PHP_ME(pqres, fetchCol, ai_pqres_fetch_col, ZEND_ACC_PUBLIC) PHP_ME(pqres, fetchAll, ai_pqres_fetch_all, ZEND_ACC_PUBLIC) PHP_ME(pqres, fetchAllCols, ai_pqres_fetch_all_cols, ZEND_ACC_PUBLIC) PHP_ME(pqres, count, ai_pqres_count, ZEND_ACC_PUBLIC) PHP_ME(pqres, map, ai_pqres_map, ZEND_ACC_PUBLIC) PHP_ME(pqres, desc, ai_pqres_desc, ZEND_ACC_PUBLIC) {0} }; PHP_MSHUTDOWN_FUNCTION(pqres) { zend_hash_destroy(&php_pqres_object_prophandlers); return SUCCESS; } PHP_MINIT_FUNCTION(pqres) { zend_class_entry ce = {0}; php_pq_object_prophandler_t ph = {0}; INIT_NS_CLASS_ENTRY(ce, "pq", "Result", php_pqres_methods); php_pqres_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC); php_pqres_class_entry->create_object = php_pqres_create_object; php_pqres_class_entry->iterator_funcs.funcs = &php_pqres_iterator_funcs; php_pqres_class_entry->get_iterator = php_pqres_iterator_init; zend_class_implements(php_pqres_class_entry TSRMLS_CC, 2, zend_ce_traversable, spl_ce_Countable); memcpy(&php_pqres_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); php_pqres_object_handlers.read_property = php_pq_object_read_prop; php_pqres_object_handlers.write_property = php_pq_object_write_prop; php_pqres_object_handlers.clone_obj = NULL; php_pqres_object_handlers.get_property_ptr_ptr = NULL; php_pqres_object_handlers.get_gc = NULL; php_pqres_object_handlers.get_debug_info = php_pq_object_debug_info; php_pqres_object_handlers.get_properties = php_pq_object_properties; php_pqres_object_handlers.count_elements = php_pqres_count_elements; zend_hash_init(&php_pqres_object_prophandlers, 8, NULL, NULL, 1); zend_declare_property_null(php_pqres_class_entry, ZEND_STRL("status"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqres_object_read_status; zend_hash_add(&php_pqres_object_prophandlers, "status", sizeof("status"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_null(php_pqres_class_entry, ZEND_STRL("statusMessage"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqres_object_read_status_message; zend_hash_add(&php_pqres_object_prophandlers, "statusMessage", sizeof("statusMessage"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_null(php_pqres_class_entry, ZEND_STRL("errorMessage"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqres_object_read_error_message; zend_hash_add(&php_pqres_object_prophandlers, "errorMessage", sizeof("errorMessage"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_long(php_pqres_class_entry, ZEND_STRL("numRows"), 0, ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqres_object_read_num_rows; zend_hash_add(&php_pqres_object_prophandlers, "numRows", sizeof("numRows"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_long(php_pqres_class_entry, ZEND_STRL("numCols"), 0, ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqres_object_read_num_cols; zend_hash_add(&php_pqres_object_prophandlers, "numCols", sizeof("numCols"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_long(php_pqres_class_entry, ZEND_STRL("affectedRows"), 0, ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqres_object_read_affected_rows; zend_hash_add(&php_pqres_object_prophandlers, "affectedRows", sizeof("affectedRows"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_long(php_pqres_class_entry, ZEND_STRL("fetchType"), PHP_PQRES_FETCH_ARRAY, ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqres_object_read_fetch_type; ph.write = php_pqres_object_write_fetch_type; zend_hash_add(&php_pqres_object_prophandlers, "fetchType", sizeof("fetchType"), (void *) &ph, sizeof(ph), NULL); ph.write = NULL; zend_declare_property_long(php_pqres_class_entry, ZEND_STRL("autoConvert"), PHP_PQRES_CONV_ALL, ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqres_object_read_auto_conv; ph.write = php_pqres_object_write_auto_conv; zend_hash_add(&php_pqres_object_prophandlers, "autoConvert", sizeof("autoConvert"), (void *) &ph, sizeof(ph), NULL); ph.write = NULL; zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("EMPTY_QUERY"), PGRES_EMPTY_QUERY TSRMLS_CC); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("COMMAND_OK"), PGRES_COMMAND_OK TSRMLS_CC); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("TUPLES_OK"), PGRES_TUPLES_OK TSRMLS_CC); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("COPY_OUT"), PGRES_COPY_OUT TSRMLS_CC); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("COPY_IN"), PGRES_COPY_IN TSRMLS_CC); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("BAD_RESPONSE"), PGRES_BAD_RESPONSE TSRMLS_CC); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("NONFATAL_ERROR"), PGRES_NONFATAL_ERROR TSRMLS_CC); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("FATAL_ERROR"), PGRES_FATAL_ERROR TSRMLS_CC); #ifdef HAVE_PGRES_COPY_BOTH zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("COPY_BOTH"), PGRES_COPY_BOTH TSRMLS_CC); #endif #ifdef HAVE_PGRES_SINGLE_TUPLE zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("SINGLE_TUPLE"), PGRES_SINGLE_TUPLE TSRMLS_CC); #endif zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("FETCH_ARRAY"), PHP_PQRES_FETCH_ARRAY TSRMLS_CC); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("FETCH_ASSOC"), PHP_PQRES_FETCH_ASSOC TSRMLS_CC); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("FETCH_OBJECT"), PHP_PQRES_FETCH_OBJECT TSRMLS_CC); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_BOOL"), PHP_PQRES_CONV_BOOL TSRMLS_CC); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_INT"), PHP_PQRES_CONV_INT TSRMLS_CC); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_FLOAT"), PHP_PQRES_CONV_FLOAT TSRMLS_CC); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_SCALAR"), PHP_PQRES_CONV_SCALAR TSRMLS_CC); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_ARRAY"), PHP_PQRES_CONV_ARRAY TSRMLS_CC); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_DATETIME"), PHP_PQRES_CONV_DATETIME TSRMLS_CC); #if PHP_PQ_HAVE_PHP_JSON_H zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_JSON"), PHP_PQRES_CONV_JSON TSRMLS_CC); #endif zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_ALL"), PHP_PQRES_CONV_ALL TSRMLS_CC); return SUCCESS; } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifndef PHP_PQRES_H #define PHP_PQRES_H #include "php_pqconn.h" typedef enum php_pqres_fetch { PHP_PQRES_FETCH_ARRAY, PHP_PQRES_FETCH_ASSOC, PHP_PQRES_FETCH_OBJECT } php_pqres_fetch_t; #define PHP_PQRES_CONV_BOOL 0x0001 #define PHP_PQRES_CONV_INT 0x0002 #define PHP_PQRES_CONV_FLOAT 0x0004 #define PHP_PQRES_CONV_SCALAR 0x000f #define PHP_PQRES_CONV_ARRAY 0x0010 #define PHP_PQRES_CONV_DATETIME 0x0020 #define PHP_PQRES_CONV_JSON 0x0100 #define PHP_PQRES_CONV_ALL 0xffff typedef struct php_pqres_iterator { zend_object_iterator zi; zval *current_val; unsigned index; php_pqres_fetch_t fetch_type; } php_pqres_iterator_t; typedef struct php_pqres { PGresult *res; php_pqres_iterator_t *iter; HashTable bound; HashTable converters; unsigned auto_convert; php_pqres_fetch_t default_fetch_type; } php_pqres_t; typedef struct php_pqres_object { zend_object zo; zend_object_value zv; HashTable *prophandler; php_pqres_t *intern; } php_pqres_object_t; extern STATUS php_pqres_success(PGresult *res TSRMLS_DC); extern void php_pqres_init_instance_data(PGresult *res, php_pqconn_object_t *obj, php_pqres_object_t **ptr TSRMLS_DC); extern zval *php_pqres_row_to_zval(PGresult *res, unsigned row, php_pqres_fetch_t fetch_type, zval **data_ptr TSRMLS_DC); extern zval *php_pqres_typed_zval(php_pqres_t *res, char *val, size_t len, Oid typ TSRMLS_DC); extern php_pqres_fetch_t php_pqres_fetch_type(php_pqres_t *res); #include "php_pq_object.h" #include "php_pqconn_event.h" #define PHP_PQclear(_r) do { \ php_pqres_object_t *_o = PQresultInstanceData((_r), php_pqconn_event); \ if (_o) { \ php_pq_object_delref(_o TSRMLS_CC); \ } else { \ PQclear(_r); \ } \ } while(0) extern zend_class_entry *php_pqres_class_entry; extern zend_object_value php_pqres_create_object_ex(zend_class_entry *ce, php_pqres_t *intern, php_pqres_object_t **ptr TSRMLS_DC); extern PHP_MINIT_FUNCTION(pqres); extern PHP_MSHUTDOWN_FUNCTION(pqres); #endif /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <php.h> #include <ext/standard/php_smart_str.h> #include "php_pq.h" #include "php_pq_misc.h" #include "php_pq_object.h" #include "php_pqexc.h" #include "php_pqconn.h" #include "php_pqres.h" #include "php_pqstm.h" zend_class_entry *php_pqstm_class_entry; static zend_object_handlers php_pqstm_object_handlers; static HashTable php_pqstm_object_prophandlers; static void php_pqstm_deallocate(php_pqstm_object_t *obj, zend_bool async, zend_bool silent TSRMLS_DC) { if (obj->intern->allocated) { char *quoted_name = PQescapeIdentifier(obj->intern->conn->intern->conn, obj->intern->name, strlen(obj->intern->name)); if (quoted_name) { smart_str cmd = {0}; smart_str_appends(&cmd, "DEALLOCATE "); smart_str_appends(&cmd, quoted_name); smart_str_0(&cmd); if (async) { if (PQsendQuery(obj->intern->conn->intern->conn, cmd.c)) { obj->intern->conn->intern->poller = PQconsumeInput; php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } else if (!silent) { throw_exce(EX_IO TSRMLS_CC, "Failed to deallocate statement (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } } else { PGresult *res; if ((res = PQexec(obj->intern->conn->intern->conn, cmd.c))) { PHP_PQclear(res); } else if (!silent) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to deallocate statement (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } } PQfreemem(quoted_name); smart_str_free(&cmd); } obj->intern->allocated = 0; } } static void php_pqstm_object_free(void *o TSRMLS_DC) { php_pqstm_object_t *obj = o; #if DBG_GC fprintf(stderr, "FREE stm(#%d) %p (conn(#%d): %p)\n", obj->zv.handle, obj, obj->intern->conn->zv.handle, obj->intern->conn); #endif if (obj->intern) { if (obj->intern->conn->intern) { php_pq_callback_dtor(&obj->intern->conn->intern->onevent); php_pqstm_deallocate(obj, 0, 1 TSRMLS_CC); php_pq_object_delref(obj->intern->conn TSRMLS_CC); } efree(obj->intern->name); efree(obj->intern->query); zend_hash_destroy(&obj->intern->bound); if (obj->intern->params) { php_pq_params_free(&obj->intern->params); } efree(obj->intern); obj->intern = NULL; } zend_object_std_dtor((zend_object *) o TSRMLS_CC); efree(obj); } zend_object_value php_pqstm_create_object_ex(zend_class_entry *ce, php_pqstm_t *intern, php_pqstm_object_t **ptr TSRMLS_DC) { php_pqstm_object_t *o; o = ecalloc(1, sizeof(*o)); zend_object_std_init((zend_object *) o, ce TSRMLS_CC); object_properties_init((zend_object *) o, ce); o->prophandler = &php_pqstm_object_prophandlers; if (ptr) { *ptr = o; } if (intern) { o->intern = intern; } o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqstm_object_free, NULL TSRMLS_CC); o->zv.handlers = &php_pqstm_object_handlers; return o->zv; } static zend_object_value php_pqstm_create_object(zend_class_entry *class_type TSRMLS_DC) { return php_pqstm_create_object_ex(class_type, NULL, NULL TSRMLS_CC); } static void php_pqstm_object_read_name(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqstm_object_t *obj = o; RETVAL_STRING(obj->intern->name, 1); } static void php_pqstm_object_read_connection(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqstm_object_t *obj = o; php_pq_object_to_zval(obj->intern->conn, &return_value TSRMLS_CC); } static void php_pqstm_object_read_query(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqstm_object_t *obj = o; RETVAL_STRING(obj->intern->query, 1); } static void php_pqstm_object_read_types(zval *object, void *o, zval *return_value TSRMLS_DC) { int i; php_pqstm_object_t *obj = o; array_init_size(return_value, obj->intern->params->type.count); for (i = 0; i < obj->intern->params->type.count; i++) { add_next_index_long(return_value, (long)obj->intern->params->type.oids[i]); } } php_pqstm_t *php_pqstm_init(php_pqconn_object_t *conn, const char *name, const char *query, php_pq_params_t *params TSRMLS_DC) { php_pqstm_t *stm = ecalloc(1, sizeof(*stm)); php_pq_object_addref(conn TSRMLS_CC); stm->conn = conn; stm->name = estrdup(name); stm->params = params; stm->query = estrdup(query); stm->allocated = 1; ZEND_INIT_SYMTABLE(&stm->bound); return stm; } ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_construct, 0, 0, 3) ZEND_ARG_OBJ_INFO(0, connection, pq\\Connection, 0) ZEND_ARG_INFO(0, name) ZEND_ARG_INFO(0, query) ZEND_ARG_ARRAY_INFO(0, types, 1) ZEND_ARG_INFO(0, async) ZEND_END_ARG_INFO(); static PHP_METHOD(pqstm, __construct) { zend_error_handling zeh; zval *zconn, *ztypes = NULL; char *name_str, *query_str; int name_len, *query_len; zend_bool async = 0; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Oss|a/!b", &zconn, php_pqconn_class_entry, &name_str, &name_len, &query_str, &query_len, &ztypes, &async); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); php_pqconn_object_t *conn_obj = zend_object_store_get_object(zconn TSRMLS_CC); if (obj->intern) { throw_exce(EX_BAD_METHODCALL TSRMLS_CC, "pq\\Statement already initialized"); } else if (!conn_obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { php_pq_params_t *params = php_pq_params_init(&conn_obj->intern->converters, ztypes ? Z_ARRVAL_P(ztypes) : NULL, NULL TSRMLS_CC); if (async) { rv = php_pqconn_prepare_async(zconn, conn_obj, name_str, query_str, params TSRMLS_CC); } else { rv = php_pqconn_prepare(zconn, conn_obj, name_str, query_str, params TSRMLS_CC); } if (SUCCESS == rv) { obj->intern = php_pqstm_init(conn_obj, name_str, query_str, params TSRMLS_CC); } } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_bind, 0, 0, 2) ZEND_ARG_INFO(0, param_no) ZEND_ARG_INFO(1, param_ref) ZEND_END_ARG_INFO(); static PHP_METHOD(pqstm, bind) { long param_no; zval **param_ref; zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lZ", ¶m_no, ¶m_ref); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Statement not initialized"); } else if (!obj->intern->allocated) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Statement has been deallocated"); } else { SEPARATE_ZVAL_TO_MAKE_IS_REF(param_ref); Z_ADDREF_PP(param_ref); zend_hash_index_update(&obj->intern->bound, param_no, (void *) param_ref, sizeof(zval *), NULL); zend_hash_sort(&obj->intern->bound, zend_qsort, php_pq_compare_index, 0 TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_exec, 0, 0, 0) ZEND_ARG_ARRAY_INFO(0, params, 1) ZEND_END_ARG_INFO(); static PHP_METHOD(pqstm, exec) { zend_error_handling zeh; zval *zparams = NULL; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a/!", &zparams); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Statement not initialized"); } else if (!obj->intern->allocated) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Statement has been deallocated"); } else { PGresult *res; php_pq_params_set_params(obj->intern->params, zparams ? Z_ARRVAL_P(zparams) : &obj->intern->bound); res = PQexecPrepared(obj->intern->conn->intern->conn, obj->intern->name, obj->intern->params->param.count, (const char *const*) obj->intern->params->param.strings, NULL, NULL, 0); php_pq_params_set_params(obj->intern->params, NULL); if (!res) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to execute statement (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { php_pq_object_to_zval_no_addref(PQresultInstanceData(res, php_pqconn_event), &return_value TSRMLS_CC); php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_exec_async, 0, 0, 0) ZEND_ARG_ARRAY_INFO(0, params, 1) ZEND_ARG_INFO(0, callable) ZEND_END_ARG_INFO(); static PHP_METHOD(pqstm, execAsync) { zend_error_handling zeh; zval *zparams = NULL; php_pq_callback_t resolver = {{0}}; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a/!f", &zparams, &resolver.fci, &resolver.fcc); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Statement not initialized"); } else if (!obj->intern->allocated) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Statement has been deallocated"); } else { int rc; php_pq_params_set_params(obj->intern->params, zparams ? Z_ARRVAL_P(zparams) : &obj->intern->bound); rc = PQsendQueryPrepared(obj->intern->conn->intern->conn, obj->intern->name, obj->intern->params->param.count, (const char *const*) obj->intern->params->param.strings, NULL, NULL, 0); php_pq_params_set_params(obj->intern->params, NULL); if (!rc) { throw_exce(EX_IO TSRMLS_CC, "Failed to execute statement (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); #if HAVE_PQSETSINGLEROWMODE } else if (obj->intern->conn->intern->unbuffered && !PQsetSingleRowMode(obj->intern->conn->intern->conn)) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); #endif } else { php_pq_callback_recurse(&obj->intern->conn->intern->onevent, &resolver TSRMLS_CC); obj->intern->conn->intern->poller = PQconsumeInput; } php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_desc, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqstm, desc) { zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters_none(); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Statement not initialized"); } else if (!obj->intern->allocated) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Statement has been deallocated"); } else { PGresult *res = PQdescribePrepared(obj->intern->conn->intern->conn, obj->intern->name); if (!res) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to describe statement (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { int p, params; array_init(return_value); for (p = 0, params = PQnparams(res); p < params; ++p) { add_next_index_long(return_value, PQparamtype(res, p)); } } PHP_PQclear(res); php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_desc_async, 0, 0, 1) ZEND_ARG_INFO(0, callable) ZEND_END_ARG_INFO(); static PHP_METHOD(pqstm, descAsync) { zend_error_handling zeh; php_pq_callback_t resolver = {{0}}; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f", &resolver.fci, &resolver.fcc); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Statement not initialized"); } else if (!obj->intern->allocated) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Statement has been deallocated"); } else if (!PQsendDescribePrepared(obj->intern->conn->intern->conn, obj->intern->name)) { throw_exce(EX_IO TSRMLS_CC, "Failed to describe statement: %s", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { php_pq_callback_recurse(&obj->intern->conn->intern->onevent, &resolver TSRMLS_CC); obj->intern->conn->intern->poller = PQconsumeInput; php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } } static zend_always_inline void php_pqstm_deallocate_handler(INTERNAL_FUNCTION_PARAMETERS, zend_bool async) { zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters_none(); zend_restore_error_handling(&zeh TSRMLS_CC); if (rv == SUCCESS) { php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Statement not initialized"); } else { php_pqstm_deallocate(obj, async, 0 TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_deallocate, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqstm, deallocate) { php_pqstm_deallocate_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); } ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_deallocate_async, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqstm, deallocateAsync) { php_pqstm_deallocate_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); } static zend_always_inline void php_pqstm_prepare_handler(INTERNAL_FUNCTION_PARAMETERS, zend_bool async) { zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters_none(); zend_restore_error_handling(&zeh TSRMLS_CC); if (rv == SUCCESS) { php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Statement not initialized"); } else if (!obj->intern->allocated) { if (async) { rv = php_pqconn_prepare_async(NULL, obj->intern->conn, obj->intern->name, obj->intern->query, obj->intern->params TSRMLS_CC); } else { rv = php_pqconn_prepare(NULL, obj->intern->conn, obj->intern->name, obj->intern->query, obj->intern->params TSRMLS_CC); } if (SUCCESS == rv) { obj->intern->allocated = 1; } } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_prepare, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqstm, prepare) { php_pqstm_prepare_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); } ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_prepare_async, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqstm, prepareAsync) { php_pqstm_prepare_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); } static zend_function_entry php_pqstm_methods[] = { PHP_ME(pqstm, __construct, ai_pqstm_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) PHP_ME(pqstm, bind, ai_pqstm_bind, ZEND_ACC_PUBLIC) PHP_ME(pqstm, deallocate, ai_pqstm_deallocate, ZEND_ACC_PUBLIC) PHP_ME(pqstm, deallocateAsync, ai_pqstm_deallocate_async, ZEND_ACC_PUBLIC) PHP_ME(pqstm, desc, ai_pqstm_desc, ZEND_ACC_PUBLIC) PHP_ME(pqstm, descAsync, ai_pqstm_desc_async, ZEND_ACC_PUBLIC) PHP_ME(pqstm, exec, ai_pqstm_exec, ZEND_ACC_PUBLIC) PHP_ME(pqstm, execAsync, ai_pqstm_exec_async, ZEND_ACC_PUBLIC) PHP_ME(pqstm, prepare, ai_pqstm_prepare, ZEND_ACC_PUBLIC) PHP_ME(pqstm, prepareAsync, ai_pqstm_prepare_async, ZEND_ACC_PUBLIC) {0} }; PHP_MSHUTDOWN_FUNCTION(pqstm) { zend_hash_destroy(&php_pqstm_object_prophandlers); return SUCCESS; } PHP_MINIT_FUNCTION(pqstm) { zend_class_entry ce = {0}; php_pq_object_prophandler_t ph = {0}; INIT_NS_CLASS_ENTRY(ce, "pq", "Statement", php_pqstm_methods); php_pqstm_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC); php_pqstm_class_entry->create_object = php_pqstm_create_object; memcpy(&php_pqstm_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); php_pqstm_object_handlers.read_property = php_pq_object_read_prop; php_pqstm_object_handlers.write_property = php_pq_object_write_prop; php_pqstm_object_handlers.clone_obj = NULL; php_pqstm_object_handlers.get_property_ptr_ptr = NULL; php_pqstm_object_handlers.get_gc = NULL; php_pqstm_object_handlers.get_properties = php_pq_object_properties; php_pqstm_object_handlers.get_debug_info = php_pq_object_debug_info; zend_hash_init(&php_pqstm_object_prophandlers, 2, NULL, NULL, 1); zend_declare_property_null(php_pqstm_class_entry, ZEND_STRL("name"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqstm_object_read_name; zend_hash_add(&php_pqstm_object_prophandlers, "name", sizeof("name"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_null(php_pqstm_class_entry, ZEND_STRL("connection"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqstm_object_read_connection; zend_hash_add(&php_pqstm_object_prophandlers, "connection", sizeof("connection"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_null(php_pqstm_class_entry, ZEND_STRL("query"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqstm_object_read_query; zend_hash_add(&php_pqstm_object_prophandlers, "query", sizeof("query"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_null(php_pqstm_class_entry, ZEND_STRL("types"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqstm_object_read_types; zend_hash_add(&php_pqstm_object_prophandlers, "types", sizeof("types"), (void *) &ph, sizeof(ph), NULL); return SUCCESS; } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifndef PHP_PQSTM_H #define PHP_PQSTM_H #include "php_pqconn.h" typedef struct php_pqstm { php_pqconn_object_t *conn; char *name; HashTable bound; php_pq_params_t *params; char *query; unsigned allocated:1; } php_pqstm_t; typedef struct php_pqstm_object { zend_object zo; zend_object_value zv; HashTable *prophandler; php_pqstm_t *intern; } php_pqstm_object_t; extern zend_class_entry *php_pqstm_class_entry; extern zend_object_value php_pqstm_create_object_ex(zend_class_entry *ce, php_pqstm_t *intern, php_pqstm_object_t **ptr TSRMLS_DC); extern php_pqstm_t *php_pqstm_init(php_pqconn_object_t *conn, const char *name, const char *query, php_pq_params_t *params TSRMLS_DC); extern PHP_MINIT_FUNCTION(pqstm); extern PHP_MSHUTDOWN_FUNCTION(pqstm); #endif /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <php.h> #include <ext/standard/php_smart_str.h> #include <libpq-events.h> #include <libpq/libpq-fs.h> #include "php_pq.h" #include "php_pq_misc.h" #include "php_pq_object.h" #include "php_pqexc.h" #include "php_pqres.h" #include "php_pqlob.h" #include "php_pqtxn.h" zend_class_entry *php_pqtxn_class_entry; static zend_object_handlers php_pqtxn_object_handlers; static HashTable php_pqtxn_object_prophandlers; const char *php_pq_isolation_level(long *isolation) { switch (*isolation) { case PHP_PQTXN_SERIALIZABLE: return "SERIALIZABLE"; case PHP_PQTXN_REPEATABLE_READ: return "REPEATABLE READ"; default: *isolation = PHP_PQTXN_READ_COMMITTED; /* no break */ case PHP_PQTXN_READ_COMMITTED: return "READ COMMITTED"; } } static void php_pqtxn_object_free(void *o TSRMLS_DC) { php_pqtxn_object_t *obj = o; #if DBG_GC fprintf(stderr, "FREE txn(#%d) %p (conn(#%d): %p)\n", obj->zv.handle, obj, obj->intern->conn->zv.handle, obj->intern->conn); #endif if (obj->intern) { if (obj->intern->open && obj->intern->conn->intern) { PGresult *res = PQexec(obj->intern->conn->intern->conn, "ROLLBACK"); if (res) { PHP_PQclear(res); } } php_pq_object_delref(obj->intern->conn TSRMLS_CC); efree(obj->intern); obj->intern = NULL; } zend_object_std_dtor((zend_object *) o TSRMLS_CC); efree(obj); } zend_object_value php_pqtxn_create_object_ex(zend_class_entry *ce, php_pqtxn_t *intern, php_pqtxn_object_t **ptr TSRMLS_DC) { php_pqtxn_object_t *o; o = ecalloc(1, sizeof(*o)); zend_object_std_init((zend_object *) o, ce TSRMLS_CC); object_properties_init((zend_object *) o, ce); o->prophandler = &php_pqtxn_object_prophandlers; if (ptr) { *ptr = o; } if (intern) { o->intern = intern; } o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqtxn_object_free, NULL TSRMLS_CC); o->zv.handlers = &php_pqtxn_object_handlers; return o->zv; } static zend_object_value php_pqtxn_create_object(zend_class_entry *class_type TSRMLS_DC) { return php_pqtxn_create_object_ex(class_type, NULL, NULL TSRMLS_CC); } static void php_pqtxn_object_read_connection(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqtxn_object_t *obj = o; php_pq_object_to_zval(obj->intern->conn, &return_value TSRMLS_CC); } static void php_pqtxn_object_read_isolation(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqtxn_object_t *obj = o; RETVAL_LONG(obj->intern->isolation); } static void php_pqtxn_object_read_readonly(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqtxn_object_t *obj = o; RETVAL_BOOL(obj->intern->readonly); } static void php_pqtxn_object_read_deferrable(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqtxn_object_t *obj = o; RETVAL_BOOL(obj->intern->deferrable); } static void php_pqtxn_object_write_isolation(zval *object, void *o, zval *value TSRMLS_DC) { php_pqtxn_object_t *obj = o; php_pqtxn_isolation_t orig = obj->intern->isolation; zval *zisolation = value; PGresult *res; if (Z_TYPE_P(zisolation) != IS_LONG) { if (Z_REFCOUNT_P(value) > 1) { zval *tmp; MAKE_STD_ZVAL(tmp); ZVAL_ZVAL(tmp, zisolation, 1, 0); convert_to_long(tmp); zisolation = tmp; } else { convert_to_long_ex(&zisolation); } } switch ((obj->intern->isolation = Z_LVAL_P(zisolation))) { case PHP_PQTXN_READ_COMMITTED: res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION ISOLATION LEVEL READ COMMITED"); break; case PHP_PQTXN_REPEATABLE_READ: res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ"); break; case PHP_PQTXN_SERIALIZABLE: res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE"); break; default: obj->intern->isolation = orig; res = NULL; break; } if (zisolation != value) { zval_ptr_dtor(&zisolation); } if (res) { php_pqres_success(res TSRMLS_CC); PHP_PQclear(res); } } static void php_pqtxn_object_write_readonly(zval *object, void *o, zval *value TSRMLS_DC) { php_pqtxn_object_t *obj = o; PGresult *res; if ((obj->intern->readonly = z_is_true(value))) { res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION READ ONLY"); } else { res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION READ WRITE"); } if (res) { php_pqres_success(res TSRMLS_CC); PHP_PQclear(res); } } static void php_pqtxn_object_write_deferrable(zval *object, void *o, zval *value TSRMLS_DC) { php_pqtxn_object_t *obj = o; PGresult *res; if ((obj->intern->deferrable = z_is_true(value))) { res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION DEFERRABLE"); } else { res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION NOT DEFERRABLE"); } if (res) { php_pqres_success(res TSRMLS_CC); PHP_PQclear(res); } } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_construct, 0, 0, 1) ZEND_ARG_OBJ_INFO(0, connection, pq\\Connection, 0) ZEND_ARG_INFO(0, async) ZEND_ARG_INFO(0, isolation) ZEND_ARG_INFO(0, readonly) ZEND_ARG_INFO(0, deferrable) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, __construct) { zend_error_handling zeh; zval *zconn; long isolation = PHP_PQTXN_READ_COMMITTED; zend_bool async = 0, readonly = 0, deferrable = 0; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|blbb", &zconn, php_pqconn_class_entry, &async, &isolation, &readonly, &deferrable); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *conn_obj = zend_object_store_get_object(zconn TSRMLS_CC); if (!conn_obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { switch (ZEND_NUM_ARGS()) { case 1: case 2: isolation = conn_obj->intern->default_txn_isolation; /* no break */ case 3: readonly = conn_obj->intern->default_txn_readonly; /* no break */ case 4: deferrable = conn_obj->intern->default_txn_deferrable; break; } if (async) { rv = php_pqconn_start_transaction_async(zconn, conn_obj, isolation, readonly, deferrable TSRMLS_CC); } else { rv = php_pqconn_start_transaction(zconn, conn_obj, isolation, readonly, deferrable TSRMLS_CC); } if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); obj->intern = ecalloc(1, sizeof(*obj->intern)); php_pq_object_addref(conn_obj TSRMLS_CC); obj->intern->conn = conn_obj; obj->intern->open = 1; obj->intern->isolation = isolation; obj->intern->readonly = readonly; obj->intern->deferrable = deferrable; } } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_savepoint, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, savepoint) { zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters_none(); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); } else if (!obj->intern->open) { throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed"); } else { PGresult *res; smart_str cmd = {0}; smart_str_appends(&cmd, "SAVEPOINT \""); smart_str_append_unsigned(&cmd, ++obj->intern->savepoint); smart_str_appends(&cmd, "\""); smart_str_0(&cmd); res = PQexec(obj->intern->conn->intern->conn, cmd.c); if (!res) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to create %s (%s)", cmd.c, PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { php_pqres_success(res TSRMLS_CC); PHP_PQclear(res); } smart_str_free(&cmd); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_savepoint_async, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, savepointAsync) { zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters_none(); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); } else if (!obj->intern->open) { throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed"); } else { smart_str cmd = {0}; smart_str_appends(&cmd, "SAVEPOINT \""); smart_str_append_unsigned(&cmd, ++obj->intern->savepoint); smart_str_appends(&cmd, "\""); smart_str_0(&cmd); if (!PQsendQuery(obj->intern->conn->intern->conn, cmd.c)) { throw_exce(EX_IO TSRMLS_CC, "Failed to create %s (%s)", cmd.c, PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } smart_str_free(&cmd); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_commit, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, commit) { zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters_none(); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transacation not initialized"); } else if (!obj->intern->open) { throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed"); } else { PGresult *res; smart_str cmd = {0}; if (!obj->intern->savepoint) { res = PQexec(obj->intern->conn->intern->conn, "COMMIT"); } else { smart_str_appends(&cmd, "RELEASE SAVEPOINT \""); smart_str_append_unsigned(&cmd, obj->intern->savepoint--); smart_str_appends(&cmd, "\""); smart_str_0(&cmd); res = PQexec(obj->intern->conn->intern->conn, cmd.c); } if (!res) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to %s (%s)", cmd.c ? cmd.c : "commit transaction", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { if (!cmd.c) { obj->intern->open = 0; } } PHP_PQclear(res); } smart_str_free(&cmd); php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_commit_async, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, commitAsync) { zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters_none(); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); } else if (!obj->intern->open) { throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed"); } else { int rc; smart_str cmd = {0}; if (!obj->intern->savepoint) { rc = PQsendQuery(obj->intern->conn->intern->conn, "COMMIT"); } else { smart_str_appends(&cmd, "RELEASE SAVEPOINT \""); smart_str_append_unsigned(&cmd, obj->intern->savepoint--); smart_str_appends(&cmd, "\""); smart_str_0(&cmd); rc = PQsendQuery(obj->intern->conn->intern->conn, cmd.c); } if (!rc) { throw_exce(EX_IO TSRMLS_CC, "Failed to %s (%s)", cmd.c ? cmd.c : "commmit transaction", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { if (!cmd.c) { obj->intern->open = 0; } obj->intern->conn->intern->poller = PQconsumeInput; php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } smart_str_free(&cmd); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_rollback, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, rollback) { zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters_none(); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); } else if (!obj->intern->open) { throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed"); } else { PGresult *res; smart_str cmd = {0}; if (!obj->intern->savepoint) { res = PQexec(obj->intern->conn->intern->conn, "ROLLBACK"); } else { smart_str_appends(&cmd, "ROLLBACK TO SAVEPOINT \""); smart_str_append_unsigned(&cmd, obj->intern->savepoint--); smart_str_appends(&cmd, "\""); smart_str_0(&cmd); res = PQexec(obj->intern->conn->intern->conn, cmd.c); } if (!res) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to %s (%s)", cmd.c ? cmd.c : "rollback transaction", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { if (!cmd.c) { obj->intern->open = 0; } } PHP_PQclear(res); } smart_str_free(&cmd); php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_rollback_async, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, rollbackAsync) { zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters_none(); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); } else if (!obj->intern->open) { throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed"); } else { int rc; smart_str cmd = {0}; if (!obj->intern->savepoint) { rc = PQsendQuery(obj->intern->conn->intern->conn, "ROLLBACK"); } else { smart_str_appends(&cmd, "ROLLBACK TO SAVEPOINT \""); smart_str_append_unsigned(&cmd, obj->intern->savepoint--); smart_str_appends(&cmd, "\""); smart_str_0(&cmd); rc = PQsendQuery(obj->intern->conn->intern->conn, cmd.c); } if (!rc) { throw_exce(EX_IO TSRMLS_CC, "Failed to %s (%s)", cmd.c ? cmd.c : "rollback transaction", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { if (!cmd.c) { obj->intern->open = 0; } obj->intern->conn->intern->poller = PQconsumeInput; } smart_str_free(&cmd); php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_export_snapshot, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, exportSnapshot) { zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters_none(); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); } else { PGresult *res = PQexec(obj->intern->conn->intern->conn, "SELECT pg_export_snapshot()"); if (!res) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to export transaction snapshot (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { RETVAL_STRING(PQgetvalue(res, 0, 0), 1); } PHP_PQclear(res); } php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_export_snapshot_async, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, exportSnapshotAsync) { zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters_none(); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); } else if (!PQsendQuery(obj->intern->conn->intern->conn, "SELECT pg_export_snapshot()")) { throw_exce(EX_IO TSRMLS_CC, "Failed to export transaction snapshot (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { obj->intern->conn->intern->poller = PQconsumeInput; php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_import_snapshot, 0, 0, 1) ZEND_ARG_INFO(0, snapshot_id) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, importSnapshot) { zend_error_handling zeh; char *snapshot_str; int snapshot_len; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &snapshot_str, &snapshot_len); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); } else if (obj->intern->isolation < PHP_PQTXN_REPEATABLE_READ) { throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction must have at least isolation level REPEATABLE READ to be able to import a snapshot"); } else { char *sid = PQescapeLiteral(obj->intern->conn->intern->conn, snapshot_str, snapshot_len); if (!sid) { throw_exce(EX_ESCAPE TSRMLS_CC, "Failed to quote snapshot identifier (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { PGresult *res; smart_str cmd = {0}; smart_str_appends(&cmd, "SET TRANSACTION SNAPSHOT "); smart_str_appends(&cmd, sid); smart_str_0(&cmd); res = PQexec(obj->intern->conn->intern->conn, cmd.c); if (!res) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to import transaction snapshot (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { php_pqres_success(res TSRMLS_CC); PHP_PQclear(res); } smart_str_free(&cmd); php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_import_snapshot_async, 0, 0, 1) ZEND_ARG_INFO(0, snapshot_id) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, importSnapshotAsync) { zend_error_handling zeh; char *snapshot_str; int snapshot_len; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &snapshot_str, &snapshot_len); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); } else if (obj->intern->isolation < PHP_PQTXN_REPEATABLE_READ) { throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction must have at least isolation level REPEATABLE READ to be able to import a snapshot"); } else { char *sid = PQescapeLiteral(obj->intern->conn->intern->conn, snapshot_str, snapshot_len); if (!sid) { throw_exce(EX_ESCAPE TSRMLS_CC, "Failed to quote snapshot identifier (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { smart_str cmd = {0}; smart_str_appends(&cmd, "SET TRANSACTION SNAPSHOT "); smart_str_appends(&cmd, sid); smart_str_0(&cmd); if (!PQsendQuery(obj->intern->conn->intern->conn, cmd.c)) { throw_exce(EX_IO TSRMLS_CC, "Failed to %s (%s)", cmd.c, PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { obj->intern->conn->intern->poller = PQconsumeInput; } smart_str_free(&cmd); php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_open_lob, 0, 0, 1) ZEND_ARG_INFO(0, oid) ZEND_ARG_INFO(0, mode) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, openLOB) { zend_error_handling zeh; long mode = INV_WRITE|INV_READ, loid; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|l", &loid, &mode); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); } else { int lofd = lo_open(obj->intern->conn->intern->conn, loid, mode); if (lofd < 0) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to open large object with oid=%u with mode '%s' (%s)", loid, php_pq_strmode(mode), PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { php_pqlob_t *lob = ecalloc(1, sizeof(*lob)); lob->lofd = lofd; lob->loid = loid; php_pq_object_addref(obj TSRMLS_CC); lob->txn = obj; return_value->type = IS_OBJECT; return_value->value.obj = php_pqlob_create_object_ex(php_pqlob_class_entry, lob, NULL TSRMLS_CC); } php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_create_lob, 0, 0, 0) ZEND_ARG_INFO(0, mode) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, createLOB) { zend_error_handling zeh; long mode = INV_WRITE|INV_READ; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &mode); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); } else { Oid loid = lo_creat(obj->intern->conn->intern->conn, mode); if (loid == InvalidOid) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to create large object with mode '%s' (%s)", php_pq_strmode(mode), PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { int lofd = lo_open(obj->intern->conn->intern->conn, loid, mode); if (lofd < 0) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to open large object with oid=%u with mode '%s': %s", loid, php_pq_strmode(mode), PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { php_pqlob_t *lob = ecalloc(1, sizeof(*lob)); lob->lofd = lofd; lob->loid = loid; php_pq_object_addref(obj TSRMLS_CC); lob->txn = obj; return_value->type = IS_OBJECT; return_value->value.obj = php_pqlob_create_object_ex(php_pqlob_class_entry, lob, NULL TSRMLS_CC); } } php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_unlink_lob, 0, 0, 1) ZEND_ARG_INFO(0, oid) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, unlinkLOB) { zend_error_handling zeh; long loid; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &loid); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); } else { int rc = lo_unlink(obj->intern->conn->intern->conn, loid); if (rc != 1) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to unlink LOB (oid=%u): %s", loid, PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_import_lob, 0, 0, 1) ZEND_ARG_INFO(0, local_path) ZEND_ARG_INFO(0, oid) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, importLOB) { zend_error_handling zeh; char *path_str; int path_len; long oid = InvalidOid; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p|l", &path_str, &path_len, &oid); zend_restore_error_handling(&zeh TSRMLS_CC); if (rv == SUCCESS) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); } else { if (oid == InvalidOid) { oid = lo_import(obj->intern->conn->intern->conn, path_str); } else { oid = lo_import_with_oid(obj->intern->conn->intern->conn, path_str, oid); } if (oid == InvalidOid) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to import LOB from '%s' (%s)", path_str, PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { RETVAL_LONG(oid); } php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_export_lob, 0, 0, 2) ZEND_ARG_INFO(0, oid) ZEND_ARG_INFO(0, local_path) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, exportLOB) { zend_error_handling zeh; char *path_str; int path_len; long oid; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lp", &oid, &path_str, &path_len); zend_restore_error_handling(&zeh TSRMLS_CC); if (rv == SUCCESS) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); } else { int rc = lo_export(obj->intern->conn->intern->conn, oid, path_str); if (rc == -1) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to export LOB (oid=%u) to '%s' (%s)", oid, path_str, PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } } static zend_function_entry php_pqtxn_methods[] = { PHP_ME(pqtxn, __construct, ai_pqtxn_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) PHP_ME(pqtxn, commit, ai_pqtxn_commit, ZEND_ACC_PUBLIC) PHP_ME(pqtxn, rollback, ai_pqtxn_rollback, ZEND_ACC_PUBLIC) PHP_ME(pqtxn, commitAsync, ai_pqtxn_commit_async, ZEND_ACC_PUBLIC) PHP_ME(pqtxn, rollbackAsync, ai_pqtxn_rollback_async, ZEND_ACC_PUBLIC) PHP_ME(pqtxn, savepoint, ai_pqtxn_savepoint, ZEND_ACC_PUBLIC) PHP_ME(pqtxn, savepointAsync, ai_pqtxn_savepoint_async, ZEND_ACC_PUBLIC) PHP_ME(pqtxn, exportSnapshot, ai_pqtxn_export_snapshot, ZEND_ACC_PUBLIC) PHP_ME(pqtxn, exportSnapshotAsync, ai_pqtxn_export_snapshot_async, ZEND_ACC_PUBLIC) PHP_ME(pqtxn, importSnapshot, ai_pqtxn_import_snapshot, ZEND_ACC_PUBLIC) PHP_ME(pqtxn, importSnapshotAsync, ai_pqtxn_import_snapshot_async, ZEND_ACC_PUBLIC) PHP_ME(pqtxn, openLOB, ai_pqtxn_open_lob, ZEND_ACC_PUBLIC) PHP_ME(pqtxn, createLOB, ai_pqtxn_create_lob, ZEND_ACC_PUBLIC) PHP_ME(pqtxn, unlinkLOB, ai_pqtxn_unlink_lob, ZEND_ACC_PUBLIC) PHP_ME(pqtxn, importLOB, ai_pqtxn_import_lob, ZEND_ACC_PUBLIC) PHP_ME(pqtxn, exportLOB, ai_pqtxn_export_lob, ZEND_ACC_PUBLIC) {0} }; PHP_MSHUTDOWN_FUNCTION(pqtxn) { zend_hash_destroy(&php_pqtxn_object_prophandlers); return SUCCESS; } PHP_MINIT_FUNCTION(pqtxn) { zend_class_entry ce = {0}; php_pq_object_prophandler_t ph = {0}; INIT_NS_CLASS_ENTRY(ce, "pq", "Transaction", php_pqtxn_methods); php_pqtxn_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC); php_pqtxn_class_entry->create_object = php_pqtxn_create_object; memcpy(&php_pqtxn_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); php_pqtxn_object_handlers.read_property = php_pq_object_read_prop; php_pqtxn_object_handlers.write_property = php_pq_object_write_prop; php_pqtxn_object_handlers.clone_obj = NULL; php_pqtxn_object_handlers.get_property_ptr_ptr = NULL; php_pqtxn_object_handlers.get_gc = NULL; php_pqtxn_object_handlers.get_properties = php_pq_object_properties; php_pqtxn_object_handlers.get_debug_info = php_pq_object_debug_info; zend_hash_init(&php_pqtxn_object_prophandlers, 4, NULL, NULL, 1); zend_declare_property_null(php_pqtxn_class_entry, ZEND_STRL("connection"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqtxn_object_read_connection; zend_hash_add(&php_pqtxn_object_prophandlers, "connection", sizeof("connection"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_null(php_pqtxn_class_entry, ZEND_STRL("isolation"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqtxn_object_read_isolation; ph.write = php_pqtxn_object_write_isolation; zend_hash_add(&php_pqtxn_object_prophandlers, "isolation", sizeof("isolation"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_bool(php_pqtxn_class_entry, ZEND_STRL("readonly"), 0, ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqtxn_object_read_readonly; ph.write = php_pqtxn_object_write_readonly; zend_hash_add(&php_pqtxn_object_prophandlers, "readonly", sizeof("readonly"), (void *) &ph, sizeof(ph), NULL); zend_declare_property_bool(php_pqtxn_class_entry, ZEND_STRL("deferrable"), 0, ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqtxn_object_read_deferrable; ph.write = php_pqtxn_object_write_deferrable; zend_hash_add(&php_pqtxn_object_prophandlers, "deferrable", sizeof("deferrable"), (void *) &ph, sizeof(ph), NULL); ph.write = NULL; zend_declare_class_constant_long(php_pqtxn_class_entry, ZEND_STRL("READ_COMMITTED"), PHP_PQTXN_READ_COMMITTED TSRMLS_CC); zend_declare_class_constant_long(php_pqtxn_class_entry, ZEND_STRL("REPEATABLE_READ"), PHP_PQTXN_REPEATABLE_READ TSRMLS_CC); zend_declare_class_constant_long(php_pqtxn_class_entry, ZEND_STRL("SERIALIZABLE"), PHP_PQTXN_SERIALIZABLE TSRMLS_CC); return SUCCESS; } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifndef PHP_PQTXN_H #define PHP_PQTXN_H #include "php_pqconn.h" typedef enum php_pqtxn_isolation { PHP_PQTXN_READ_COMMITTED, PHP_PQTXN_REPEATABLE_READ, PHP_PQTXN_SERIALIZABLE, } php_pqtxn_isolation_t; typedef struct php_pqtxn { php_pqconn_object_t *conn; php_pqtxn_isolation_t isolation; unsigned savepoint; unsigned open:1; unsigned readonly:1; unsigned deferrable:1; } php_pqtxn_t; typedef struct php_pqtxn_object { zend_object zo; zend_object_value zv; HashTable *prophandler; php_pqtxn_t *intern; } php_pqtxn_object_t; extern const char *php_pq_isolation_level(long *isolation); extern zend_class_entry *php_pqtxn_class_entry; extern zend_object_value php_pqtxn_create_object_ex(zend_class_entry *ce, php_pqtxn_t *intern, php_pqtxn_object_t **ptr TSRMLS_DC); extern PHP_MINIT_FUNCTION(pqtxn); extern PHP_MSHUTDOWN_FUNCTION(pqtxn); #endif /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <php.h> #include <ext/standard/php_smart_str.h> #include "php_pq.h" #include "php_pq_misc.h" #include "php_pq_object.h" #include "php_pqexc.h" #include "php_pqres.h" #include "php_pqtypes.h" zend_class_entry *php_pqtypes_class_entry; static zend_object_handlers php_pqtypes_object_handlers; static HashTable php_pqtypes_object_prophandlers; static void php_pqtypes_object_free(void *o TSRMLS_DC) { php_pqtypes_object_t *obj = o; #if DBG_GC fprintf(stderr, "FREE types(#%d) %p (conn(#%d): %p)\n", obj->zv.handle, obj, obj->intern->conn->zv.handle, obj->intern->conn); #endif if (obj->intern) { zend_hash_destroy(&obj->intern->types); php_pq_object_delref(obj->intern->conn TSRMLS_CC); efree(obj->intern); obj->intern = NULL; } zend_object_std_dtor((zend_object *) o TSRMLS_CC); efree(obj); } zend_object_value php_pqtypes_create_object_ex(zend_class_entry *ce, php_pqtypes_t *intern, php_pqtypes_object_t **ptr TSRMLS_DC) { php_pqtypes_object_t *o; o = ecalloc(1, sizeof(*o)); zend_object_std_init((zend_object *) o, ce TSRMLS_CC); object_properties_init((zend_object *) o, ce); o->prophandler = &php_pqtypes_object_prophandlers; if (ptr) { *ptr = o; } if (intern) { o->intern = intern; } o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqtypes_object_free, NULL TSRMLS_CC); o->zv.handlers = &php_pqtypes_object_handlers; return o->zv; } static zend_object_value php_pqtypes_create_object(zend_class_entry *class_type TSRMLS_DC) { return php_pqtypes_create_object_ex(class_type, NULL, NULL TSRMLS_CC); } static void php_pqtypes_object_read_connection(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqtypes_object_t *obj = o; php_pq_object_to_zval(obj->intern->conn, &return_value TSRMLS_CC); } static int has_dimension(HashTable *ht, zval *member, char **key_str, int *key_len, ulong *index TSRMLS_DC) { long lval = 0; zval *tmp = member; switch (Z_TYPE_P(member)) { default: convert_to_string_ex(&tmp); /* no break */ case IS_STRING: if (!is_numeric_string(Z_STRVAL_P(tmp), Z_STRLEN_P(tmp), &lval, NULL, 0)) { int exists = zend_hash_exists(ht, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp) + 1); if (key_str) { *key_str = estrndup(Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); if (key_len) { *key_len = Z_STRLEN_P(tmp) + 1; } } if (member != tmp) { zval_ptr_dtor(&tmp); } return exists; } break; case IS_LONG: lval = Z_LVAL_P(member); break; } if (member != tmp) { zval_ptr_dtor(&tmp); } if (index) { *index = lval; } return zend_hash_index_exists(ht, lval); } static int php_pqtypes_object_has_dimension(zval *object, zval *member, int check_empty TSRMLS_DC) { php_pqtypes_object_t *obj = zend_object_store_get_object(object TSRMLS_CC); char *key_str = NULL; int key_len = 0; ulong index = 0; if (check_empty) { if (has_dimension(&obj->intern->types, member, &key_str, &key_len, &index TSRMLS_CC)) { zval **data; if (key_str && key_len) { if (SUCCESS == zend_hash_find(&obj->intern->types, key_str, key_len, (void *) &data)) { efree(key_str); return Z_TYPE_PP(data) != IS_NULL; } efree(key_str); key_str = NULL; } else { if (SUCCESS == zend_hash_index_find(&obj->intern->types, index, (void *) &data)) { return Z_TYPE_PP(data) != IS_NULL; } } } if (key_str) { efree(key_str); } } else { return has_dimension(&obj->intern->types, member, NULL, NULL, NULL TSRMLS_CC); } return 0; } static zval *php_pqtypes_object_read_dimension(zval *object, zval *member, int type TSRMLS_DC) { ulong index = 0; char *key_str = NULL; int key_len = 0; php_pqtypes_object_t *obj = zend_object_store_get_object(object TSRMLS_CC); if (has_dimension(&obj->intern->types, member, &key_str, &key_len, &index TSRMLS_CC)) { zval **data; if (key_str && key_len) { if (SUCCESS == zend_hash_find(&obj->intern->types, key_str, key_len, (void *) &data)) { efree(key_str); return *data; } } else { if (SUCCESS == zend_hash_index_find(&obj->intern->types, index, (void *) &data)) { return *data; } } } if (key_str) { efree(key_str); } return NULL; } ZEND_BEGIN_ARG_INFO_EX(ai_pqtypes_construct, 0, 0, 1) ZEND_ARG_OBJ_INFO(0, connection, pq\\Connection, 0) ZEND_ARG_ARRAY_INFO(0, namespaces, 1) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtypes, __construct) { zend_error_handling zeh; zval *zconn, *znsp = NULL; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|a!", &zconn, php_pqconn_class_entry, &znsp); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqconn_object_t *conn_obj = zend_object_store_get_object(zconn TSRMLS_CC); if (!conn_obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { php_pqtypes_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); zval *retval = NULL; obj->intern = ecalloc(1, sizeof(*obj->intern)); obj->intern->conn = conn_obj; php_pq_object_addref(conn_obj TSRMLS_CC); zend_hash_init(&obj->intern->types, 512, NULL, ZVAL_PTR_DTOR, 0); if (znsp) { zend_call_method_with_1_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "refresh", &retval, znsp); } else { zend_call_method_with_0_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "refresh", &retval); } if (retval) { zval_ptr_dtor(&retval); } } } } #define PHP_PQ_TYPES_QUERY \ "select t.oid, t.* " \ "from pg_type t join pg_namespace n on t.typnamespace=n.oid " \ "where typisdefined " \ "and typrelid=0" #ifndef PHP_PQ_OID_TEXT # define PHP_PQ_OID_TEXT 25 #endif static int apply_nsp(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) { zval **zp = p; unsigned pcount, tcount; php_pq_params_t *params = va_arg(argv, php_pq_params_t *); smart_str *str = va_arg(argv, smart_str *); tcount = php_pq_params_add_type_oid(params, PHP_PQ_OID_TEXT); pcount = php_pq_params_add_param(params, *zp); if (tcount != pcount) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Param/Type count mismatch"); return ZEND_HASH_APPLY_STOP; } if (pcount > 1) { smart_str_appendc(str, ','); } smart_str_appendc(str, '$'); smart_str_append_unsigned(str, pcount); return ZEND_HASH_APPLY_KEEP; } ZEND_BEGIN_ARG_INFO_EX(ai_pqtypes_refresh, 0, 0, 0) ZEND_ARG_ARRAY_INFO(0, namespaces, 1) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtypes, refresh) { HashTable *nsp = NULL; zend_error_handling zeh; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|H/!", &nsp); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { php_pqtypes_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Types not initialized"); } else { PGresult *res; if (!nsp || !zend_hash_num_elements(nsp)) { res = PQexec(obj->intern->conn->intern->conn, PHP_PQ_TYPES_QUERY " and nspname in ('public', 'pg_catalog')"); } else { smart_str str = {0}; php_pq_params_t *params = php_pq_params_init(&obj->intern->conn->intern->converters, NULL, NULL TSRMLS_CC); smart_str_appends(&str, PHP_PQ_TYPES_QUERY " and nspname in("); zend_hash_apply_with_arguments(nsp TSRMLS_CC, apply_nsp, 2, params, &str); smart_str_appendc(&str, ')'); smart_str_0(&str); res = PQexecParams(obj->intern->conn->intern->conn, str.c, params->param.count, params->type.oids, (const char *const*) params->param.strings, NULL, NULL, 0); smart_str_free(&str); php_pq_params_free(¶ms); } if (!res) { throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to fetch types (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { int r, rows; for (r = 0, rows = PQntuples(res); r < rows; ++r) { zval *row = php_pqres_row_to_zval(res, r, PHP_PQRES_FETCH_OBJECT, NULL TSRMLS_CC); long oid = atol(PQgetvalue(res, r, 0 )); char *name = PQgetvalue(res, r, 1); Z_ADDREF_P(row); zend_hash_index_update(&obj->intern->types, oid, (void *) &row, sizeof(zval *), NULL); zend_hash_update(&obj->intern->types, name, strlen(name) + 1, (void *) &row, sizeof(zval *), NULL); } } PHP_PQclear(res); php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } } } static zend_function_entry php_pqtypes_methods[] = { PHP_ME(pqtypes, __construct, ai_pqtypes_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) PHP_ME(pqtypes, refresh, ai_pqtypes_refresh, ZEND_ACC_PUBLIC) {0} }; PHP_MSHUTDOWN_FUNCTION(pqtypes) { zend_hash_destroy(&php_pqtypes_object_prophandlers); return SUCCESS; } PHP_MINIT_FUNCTION(pqtypes) { zend_class_entry ce = {0}; php_pq_object_prophandler_t ph = {0}; INIT_NS_CLASS_ENTRY(ce, "pq", "Types", php_pqtypes_methods); php_pqtypes_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC); php_pqtypes_class_entry->create_object = php_pqtypes_create_object; /* zend_class_implements(php_pqtypes_class_entry TSRMLS_CC, 1, zend_ce_arrayaccess); */ memcpy(&php_pqtypes_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); php_pqtypes_object_handlers.read_property = php_pq_object_read_prop; php_pqtypes_object_handlers.write_property = php_pq_object_write_prop; php_pqtypes_object_handlers.clone_obj = NULL; php_pqtypes_object_handlers.get_property_ptr_ptr = NULL; php_pqtypes_object_handlers.get_gc = NULL; php_pqtypes_object_handlers.get_properties = php_pq_object_properties; php_pqtypes_object_handlers.get_debug_info = php_pq_object_debug_info; php_pqtypes_object_handlers.has_dimension = php_pqtypes_object_has_dimension; php_pqtypes_object_handlers.read_dimension = php_pqtypes_object_read_dimension; php_pqtypes_object_handlers.unset_dimension = NULL; php_pqtypes_object_handlers.write_dimension = NULL; zend_hash_init(&php_pqtypes_object_prophandlers, 1, NULL, NULL, 1); zend_declare_property_null(php_pqtypes_class_entry, ZEND_STRL("connection"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqtypes_object_read_connection; zend_hash_add(&php_pqtypes_object_prophandlers, "connection", sizeof("connection"), (void *) &ph, sizeof(ph), NULL); # undef PHP_PQ_TYPE # define PHP_PQ_TYPE(name, oid) zend_declare_class_constant_long(php_pqtypes_class_entry, ZEND_STRL(name), oid TSRMLS_CC); # include "php_pq_type.h" return SUCCESS; } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +--------------------------------------------------------------------+ | PECL :: pq | +--------------------------------------------------------------------+ | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ | Copyright (c) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifndef PHP_PQTYPES_H #define PHP_PQTYPES_H #include "php_pqconn.h" typedef struct php_pqtypes { HashTable types; php_pqconn_object_t *conn; } php_pqtypes_t; typedef struct php_pqtypes_object { zend_object zo; zend_object_value zv; HashTable *prophandler; php_pqtypes_t *intern; } php_pqtypes_object_t; extern zend_class_entry *php_pqtypes_class_entry; extern zend_object_value php_pqtypes_create_object_ex(zend_class_entry *ce, php_pqtypes_t *intern, php_pqtypes_object_t **ptr TSRMLS_DC); extern PHP_MINIT_FUNCTION(pqtypes); extern PHP_MSHUTDOWN_FUNCTION(pqtypes); #endif /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ --TEST-- async connect --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN, true); $s = array($c->status); echo "W"; $w = array($c->socket); $r = $e = null; stream_select($r, $w, $e, null); while (true) { $s[] = $c->status; echo "P"; switch ($c->poll()) { case pq\Connection::POLLING_READING: echo "R"; $w = $e = null; $r = array($c->socket); stream_select($r, $w, $e, NULL); break; case pq\Connection::POLLING_WRITING: echo "W"; $w = array($c->socket); $r = $e = null; stream_select($r, $w, $e, null); break; case pq\Connection::POLLING_FAILED: echo "F"; break 2; case pq\Connection::POLLING_OK: echo "S"; break 2; } } printf("\n%s\n", implode(",", $s)); ?> DONE --EXPECTREGEX-- Test (WP(RP)*)+S (2,)*3(,\d)*,4 DONE --TEST-- async reset --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN, true); function complete($c) { $s = array($c->status); echo "W"; $w = array($c->socket); $r = $e = null; stream_select($r, $w, $e, null); while (true) { $s[] = $c->status; echo "P"; switch ($c->poll()) { case pq\Connection::POLLING_READING: echo "R"; $w = $e = null; $r = array($c->socket); stream_select($r, $w, $e, NULL); break; case pq\Connection::POLLING_WRITING: echo "W"; $w = array($c->socket); $r = $e = null; stream_select($r, $w, $e, null); break; case pq\Connection::POLLING_FAILED: echo "F"; break 2; case pq\Connection::POLLING_OK: echo "S"; break 2; } } printf("\n%s\n", implode(",", $s)); } complete($c); if ($c->status == pq\Connection::OK) { $c->resetAsync(); complete($c); } ?> DONE --EXPECTREGEX-- Test (WP(RP)*)+S (2,)*3(,\d)*,4 (WP(RP)*)+S (2,)*3(,\d)*,4 DONE --TEST-- async exec --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); $c->execAsync("SELECT 1+2+3; SELECT 2,3,4", function ($res) { var_dump($res); }); do { while ($c->busy) { $r = array($c->socket); $w = $e = null; if (stream_select($r, $w, $e, null)) { $c->poll(); } } } while ($c->getResult()); ?> DONE --EXPECTF-- Test object(pq\Result)#%d (8) { ["status"]=> int(2) ["statusMessage"]=> string(9) "TUPLES_OK" ["errorMessage"]=> string(0) "" ["numRows"]=> int(1) ["numCols"]=> int(1) ["affectedRows"]=> int(%d) ["fetchType"]=> int(0) ["autoConvert"]=> int(65535) } object(pq\Result)#%d (8) { ["status"]=> int(2) ["statusMessage"]=> string(9) "TUPLES_OK" ["errorMessage"]=> string(0) "" ["numRows"]=> int(1) ["numCols"]=> int(3) ["affectedRows"]=> int(%d) ["fetchType"]=> int(0) ["autoConvert"]=> int(65535) } DONE --TEST-- async exec params --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); $t = new pq\Types($c); $c->execParamsAsync("SELECT \$1,\$2::int4", array(1,2), array($t["int4"]->oid), function ($res) { var_dump($res); }); do { while ($c->busy) { $r = array($c->socket); $w = $e = null; if (stream_select($r, $w, $e, null)) { $c->poll(); } } } while ($c->getResult()); ?> DONE --EXPECTF-- Test object(pq\Result)#%d (8) { ["status"]=> int(2) ["statusMessage"]=> string(9) "TUPLES_OK" ["errorMessage"]=> string(0) "" ["numRows"]=> int(1) ["numCols"]=> int(2) ["affectedRows"]=> int(%d) ["fetchType"]=> int(0) ["autoConvert"]=> int(65535) } DONE --TEST-- async prepared statement --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; function complete($s) { do { while ($s->connection->busy) { $r = array($s->connection->socket); $w = $e = null; if (stream_select($r, $w, $e, null)) { $s->connection->poll(); } } } while ($s->connection->getResult()); } $c = new pq\Connection(PQ_DSN); $t = new pq\Types($c); $s = $c->prepareAsync("test", "SELECT \$1,\$2::int4", array($t["int4"]->oid)); complete($s); $s->execAsync(array(1,2), function ($res) { var_dump($res); }); complete($s); ?> DONE --EXPECTF-- Test object(pq\Result)#%d (8) { ["status"]=> int(2) ["statusMessage"]=> string(9) "TUPLES_OK" ["errorMessage"]=> string(0) "" ["numRows"]=> int(1) ["numCols"]=> int(2) ["affectedRows"]=> int(%d) ["fetchType"]=> int(0) ["autoConvert"]=> int(65535) } DONE --TEST-- async unbuffered exec --SKIPIF-- <?php include "_skipif.inc"; defined("pq\\Result::SINGLE_TUPLE") or die("skip need pq\\Result::SINGLE_TUPLE"); ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); $c->unbuffered = true; $c->execAsync("SELECT a FROM generate_series(1,3) a", function ($res) { var_dump($res); }); do { while ($c->busy) { $r = array($c->socket); $w = $e = null; if (stream_select($r, $w, $e, null)) { $c->poll(); } } } while ($c->getResult()); ?> DONE --EXPECTF-- Test object(pq\Result)#%d (8) { ["status"]=> int(9) ["statusMessage"]=> string(12) "SINGLE_TUPLE" ["errorMessage"]=> string(0) "" ["numRows"]=> int(1) ["numCols"]=> int(1) ["affectedRows"]=> int(0) ["fetchType"]=> int(0) ["autoConvert"]=> int(65535) } object(pq\Result)#%d (8) { ["status"]=> int(9) ["statusMessage"]=> string(12) "SINGLE_TUPLE" ["errorMessage"]=> string(0) "" ["numRows"]=> int(1) ["numCols"]=> int(1) ["affectedRows"]=> int(0) ["fetchType"]=> int(0) ["autoConvert"]=> int(65535) } object(pq\Result)#%d (8) { ["status"]=> int(9) ["statusMessage"]=> string(12) "SINGLE_TUPLE" ["errorMessage"]=> string(0) "" ["numRows"]=> int(1) ["numCols"]=> int(1) ["affectedRows"]=> int(0) ["fetchType"]=> int(0) ["autoConvert"]=> int(65535) } object(pq\Result)#%d (8) { ["status"]=> int(2) ["statusMessage"]=> string(9) "TUPLES_OK" ["errorMessage"]=> string(0) "" ["numRows"]=> int(0) ["numCols"]=> int(1) ["affectedRows"]=> int(3) ["fetchType"]=> int(0) ["autoConvert"]=> int(65535) } DONE --TEST-- async statement --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; function complete($c) { do { while ($c->busy) { $r = array($c->socket); $w = $e = null; if (stream_select($r, $w, $e, null)) { $c->poll(); } } } while ($c->getResult()); } $c = new pq\Connection(PQ_DSN); $t = new pq\Types($c); $s = new pq\Statement($c, "test1", "SELECT NOW() - \$1", null, true); complete($s->connection); $s->execAsync(array("2012-12-12 12:12:12")); complete($s->connection); $s->descAsync(function($r) use ($t) { list($typeOid) = $r->desc(); printf("%s\n", $t[$typeOid]->typname); }); complete($s->connection); ?> DONE --EXPECT-- Test timestamptz DONE --TEST-- async cursor --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; function complete($c) { do { while ($c->busy) { $r = array($c->socket); $w = $e = null; if (stream_select($r, $w, $e, null)) { $c->poll(); } } } while ($c->getResult()); } $c = new pq\Connection(PQ_DSN); $p = $c->declareAsync("mycursor", pq\Cursor::WITH_HOLD, "SELECT * FROM generate_series(0,29) s WHERE (s%2)=0"); complete($c); do { $p->fetchAsync(2, function ($r) { foreach ($r as $row) { foreach ($row as $col) { echo " $col"; } echo "\n"; } }); complete($p->connection); $p->moveAsync(1, function ($r) use(&$keep_going) { $keep_going = $r->affectedRows; }); complete($p->connection); } while ($keep_going); ?> ===DONE=== --EXPECT-- Test 0 2 6 8 12 14 18 20 24 26 ===DONE=== --TEST-- basic functionality --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $con = new pq\Connection(PQ_DSN); $res = $con->exec("SELECT 1 as one, 2 as two from generate_series(1,2)"); var_dump($res->status == pq\Result::TUPLES_OK); var_dump($res->numRows); var_dump($res->numCols); var_dump(count($res) == $res->count(), $res->numRows == count($res)); foreach ($res as $rowNum => $rowData) { printf("%d.0 => %d\n", $rowNum, $rowData[0]); printf("%d.1 => %d\n", $rowNum, $rowData[1]); } $res->fetchType = pq\Result::FETCH_ASSOC; foreach ($res as $rowNum => $rowData) { printf("%d.0 => %d\n", $rowNum, $rowData["one"]); printf("%d.1 => %d\n", $rowNum, $rowData["two"]); } $res->fetchType = pq\Result::FETCH_OBJECT; foreach ($res as $rowNum => $rowData) { printf("%d.0 => %d\n", $rowNum, $rowData->one); printf("%d.1 => %d\n", $rowNum, $rowData->two); } ?> DONE --EXPECT-- Test bool(true) int(2) int(2) bool(true) bool(true) 0.0 => 1 0.1 => 2 1.0 => 1 1.1 => 2 0.0 => 1 0.1 => 2 1.0 => 1 1.1 => 2 0.0 => 1 0.1 => 2 1.0 => 1 1.1 => 2 DONE --TEST-- basic functionality --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); $t = new pq\Types($c); $s = $c->prepare("test1", "SELECT \$1",array($t["text"]->oid)); $r = $s->exec(array("fooo")); printf("%s\n", $r->errorMessage); $r->fetchCol($val); printf("%s\n", $val); ?> DONE --EXPECT-- Test fooo DONE --TEST-- fetch bound --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); $r = $c->exec("select 1*a,2*a,3*a from generate_series(2,3) a"); $r->bind(0, $a); $r->bind(1, $b); $r->bind(2, $c); while ($s = $r->fetchBound()) { var_dump($s,$a,$b,$c); } ?> DONE --EXPECT-- Test array(3) { [0]=> &int(2) [1]=> &int(4) [2]=> &int(6) } int(2) int(4) int(6) array(3) { [0]=> &int(3) [1]=> &int(6) [2]=> &int(9) } int(3) int(6) int(9) DONE --TEST-- cancel --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); $x = new pq\Cancel($c); $c->execAsync("SELECT pg_sleep(2)"); $x->cancel(); var_dump($c === $x->connection); var_dump($c->getResult()); printf("%s\n", $c->errorMessage); ?> DONE --EXPECTF-- Test bool(true) object(pq\Result)#%d (8) { ["status"]=> int(7) ["statusMessage"]=> string(11) "FATAL_ERROR" ["errorMessage"]=> string(47) "ERROR: canceling statement due to user request" ["numRows"]=> int(0) ["numCols"]=> int(0) ["affectedRows"]=> int(0) ["fetchType"]=> int(0) ["autoConvert"]=> int(65535) } ERROR: canceling statement due to user request DONE --TEST-- converter --SKIPIF-- <?php include "_skipif.inc"; _ext("json"); ?> --INI-- date.timezone=UTC --FILE-- <?php echo "Test\n"; include "_setup.inc"; abstract class Converter implements pq\Converter { protected $types; function __construct(\pq\Types $types) { $this->types = $types; } } class HStoreConverter extends Converter { function convertTypes() { return [ $this->types["hstore"]->oid ]; } function convertFromString($string, $type) { return eval("return [$string];"); } function convertToString($data, $type) { $string = ""; foreach ($data as $k => $v) { if (isset($v)) { $string .= sprintf("\"%s\"=>\"%s\",", addslashes($k), addslashes($v)); } else { $string .= sprintf("\"%s\"=>NULL,", addslashes($k)); } } return $string; } } class IntVectorConverter extends Converter { function convertTypes() { return [ $this->types["int2vector"]->oid, $this->types["oidvector"]->oid ]; } function convertFromString($string, $type) { return array_map("intval", explode(" ", $string)); } function convertToString($data, $type) { return implode(" ", $data); } } class JSONConverter extends Converter { function convertTypes() { return [ $this->types["json"]->oid ]; } function convertFromString($string, $type) { return json_decode($string, true); } function convertToString($data, $type) { return json_encode($data); } } class Text { private $data; function __construct($data) { $this->data = $data; } function __toString() { return (string) $this->data; } } $c = new pq\Connection(PQ_DSN); $c->exec("CREATE EXTENSION IF NOT EXISTS hstore"); $t = new pq\Types($c); $c->setConverter(new HStoreConverter($t)); $c->setConverter(new IntVectorConverter($t)); if (!defined("pq\\Types::JSON")) { $c->setConverter(new JSONConverter($t)); } $r = $c->execParams("SELECT \$1 as hs, \$2 as iv, \$3 as oids, \$4 as js, \$5 as ia, \$6 as ta, \$7 as ba, \$8 as da, \$9 as dbl, \$10 as bln, ". "\$11 as dt1, \$12 as dt2, \$13 as dt3, \$14 as dt4, \$15 as dt5, \$16 as dt6, \$17 as dt7, \$18 as dt8, \$19 as txta ", array( // hstore array( "k1" => "v1", "k2" => "v2", "k3" => null ), // vectors array( 1, 3, 5, 7, 9, 11 ), array( 2345124, 1431341, 1343423 ), // JSON (object) array( "int" => 123, "obj" => (object) array( "a" => 1, "b" => 2, "c" => 3, ), "str" => "äüö" ), // arrays array(array(array(1,2,3))), array(array("a\"","b}",null)), array(true,false), array(1.1,2.2), // double 123.456, // bool true, // datetimes new pq\Datetime, new pq\Datetime, new pq\Datetime, new pq\Datetime, new pq\Datetime, new pq\Datetime, new pq\Datetime, new pq\Datetime, [new Text(0), new Text(" or "), new Text(true)], ), array( $t["hstore"]->oid, $t["int2vector"]->oid, $t["oidvector"]->oid, $t["json"]->oid, $t["_int4"]->oid, $t["_text"]->oid, $t["_bool"]->oid, $t["_float8"]->oid, $t["float4"]->oid, $t["bool"]->oid, $t["date"]->oid, $t["abstime"]->oid, $t["timestamp"]->oid, $t["timestamptz"]->oid, $t["date"]->oid, $t["abstime"]->oid, $t["timestamp"]->oid, $t["timestamptz"]->oid, $t["_text"]->oid ) ); var_dump($r->fetchAll()); ?> Done --EXPECTF-- Test array(1) { [0]=> array(%d) { [0]=> array(3) { ["k1"]=> string(2) "v1" ["k2"]=> string(2) "v2" ["k3"]=> NULL } [1]=> array(6) { [0]=> int(1) [1]=> int(3) [2]=> int(5) [3]=> int(7) [4]=> int(9) [5]=> int(11) } [2]=> array(3) { [0]=> int(2345124) [1]=> int(1431341) [2]=> int(1343423) } [3]=> array(3) { ["int"]=> int(123) ["obj"]=> array(3) { ["a"]=> int(1) ["b"]=> int(2) ["c"]=> int(3) } ["str"]=> string(6) "äüö" } [4]=> array(1) { [0]=> array(1) { [0]=> array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) } } } [5]=> array(1) { [0]=> array(3) { [0]=> string(2) "a"" [1]=> string(2) "b}" [2]=> NULL } } [6]=> array(2) { [0]=> bool(true) [1]=> bool(false) } [7]=> array(2) { [0]=> float(1.1) [1]=> float(2.2) } [8]=> float(123.456) [9]=> bool(true) [10]=> object(pq\DateTime)#%d (4) { ["format"]=> string(5) "Y-m-d" ["date"]=> string(26) "%d-%d-%d 00:00:00.000000" ["timezone_type"]=> int(3) ["timezone"]=> string(3) "UTC" } [11]=> object(pq\DateTime)#%d (4) { ["format"]=> string(11) "Y-m-d H:i:s" ["date"]=> string(26) "%d-%d-%d %d:%d:%d.000000" ["timezone_type"]=> int(1) ["timezone"]=> string(%d) "%s" } [12]=> object(pq\DateTime)#%d (4) { ["format"]=> string(13) "Y-m-d H:i:s.u" ["date"]=> string(26) "%d-%d-%d %d:%d:%d.000000" ["timezone_type"]=> int(3) ["timezone"]=> string(3) "UTC" } [13]=> object(pq\DateTime)#%d (4) { ["format"]=> string(14) "Y-m-d H:i:s.uO" ["date"]=> string(26) "%d-%d-%d %d:%d:%d.000000" ["timezone_type"]=> int(1) ["timezone"]=> string(%d) "%s" } [14]=> object(pq\DateTime)#%d (4) { ["format"]=> string(5) "Y-m-d" ["date"]=> string(26) "%d-%d-%d 00:00:00.000000" ["timezone_type"]=> int(3) ["timezone"]=> string(3) "UTC" } [15]=> object(pq\DateTime)#%d (4) { ["format"]=> string(11) "Y-m-d H:i:s" ["date"]=> string(26) "%d-%d-%d %d:%d:%d.000000" ["timezone_type"]=> int(1) ["timezone"]=> string(%d) "%s" } [16]=> object(pq\DateTime)#%d (4) { ["format"]=> string(13) "Y-m-d H:i:s.u" ["date"]=> string(26) "%d-%d-%d %d:%d:%d.000000" ["timezone_type"]=> int(3) ["timezone"]=> string(3) "UTC" } [17]=> object(pq\DateTime)#%d (4) { ["format"]=> string(14) "Y-m-d H:i:s.uO" ["date"]=> string(26) "%d-%d-%d %d:%d:%d.000000" ["timezone_type"]=> int(1) ["timezone"]=> string(%d) "%s" } [18]=> array(3) { [0]=> string(1) "0" [1]=> string(4) " or " [2]=> string(1) "1" } } } Done --TEST-- copy --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); $c->exec("DROP TABLE IF EXISTS copy_test; CREATE TABLE copy_test (id serial, line text);"); $file = file(__FILE__); $in = new pq\COPY($c, "copy_test (line)", pq\COPY::FROM_STDIN, "DELIMITER '\t'"); var_dump( $c === $in->connection, "copy_test (line)" === $in->expression, pq\COPY::FROM_STDIN === $in->direction, "DELIMITER '\t'" === $in->options ); foreach ($file as $i => $line) { $in->put(addcslashes($line, "\\\t")); } $in->end(); $out = new pq\COPY($c, "copy_test (line)", pq\COPY::TO_STDOUT, "DELIMITER '\t'"); var_dump( $c === $out->connection, "copy_test (line)" === $out->expression, pq\COPY::TO_STDOUT === $out->direction, "DELIMITER '\t'" === $out->options ); while ($out->get($line)) { $lines[] = stripcslashes($line); } var_dump($file == $lines); if ($file != $lines) { foreach (array_keys(array_diff($file, $lines)) as $idx) { var_dump($idx, $file[$idx], $lines[$idx], "##############"); } } $c->exec("DROP TABLE copy_test"); ?> DONE --EXPECT-- Test bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) DONE --TEST-- cursor --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); $p = $c->declare("mycursor", pq\Cursor::WITH_HOLD, "SELECT * FROM generate_series(0,29) s WHERE (s%2)=0"); for ($r = $p->fetch(2); $r->numRows; $p->move(1), $r = $p->fetch(2)) { foreach ($r as $row) { foreach ($row as $col) { echo " $col"; } echo "\n"; } } try { $p = new pq\Cursor($c, "mycursor", pq\Cursor::WITH_HOLD, "SELECT * FROM generate_series(0,29) s WHERE (s%2)=0"); } catch (Exception $ex) { $p->close(); } $p = new pq\Cursor($c, "mycursor", pq\Cursor::WITH_HOLD, "SELECT * FROM generate_series(0,29) s WHERE (s%2)=0"); for ($r = $p->fetch(2); $r->numRows; $p->move(1), $r = $p->fetch(2)) { foreach ($r as $row) { foreach ($row as $col) { echo " $col"; } echo "\n"; } } ?> ===DONE=== --EXPECT-- Test 0 2 6 8 12 14 18 20 24 26 0 2 6 8 12 14 18 20 24 26 ===DONE=== --TEST-- encoding --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); var_dump($c->encoding); $c->encoding = "utf8"; var_dump($c->encoding); $c->exec("SELECT 'ßüpä…'")->fetchCol($val); var_dump($val); $tmp = 12345; $c->encoding = $tmp; var_dump($c->encoding); ?> DONE --EXPECTF-- Test string(%d) "%s" string(4) "UTF8" string(10) "ßüpä…" Notice: Unrecognized encoding '12345' in %s on line %d string(4) "UTF8" DONE --TEST-- exceptions --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; try { new pq\Connection(1,2,3,4); foo(); } catch (pq\Exception $e) { assert($e->getCode() == pq\Exception::INVALID_ARGUMENT, $e->getCode()."!=".pq\Exception::INVALID_ARGUMENT); } try { new pq\Connection(1,2,3,4); foo(); } catch (pq\Exception\InvalidArgumentException $e) { assert($e->getCode() == pq\Exception::INVALID_ARGUMENT, $e->getCode()."!=".pq\Exception::INVALID_ARGUMENT); } class c extends pq\Connection { function __construct() { } function open($dsn) { parent::__construct($dsn); } } $c = new c; try { $c->reset(); foo(); } catch (pq\Exception\BadMethodCallException $e) { assert($e->getCode() == pq\Exception::UNINITIALIZED, $e->getCode()."!=".pq\Exception::UNINITIALIZED); } $c->open(PQ_DSN); try { $c->open(PQ_DSN); foo(); } catch (pq\Exception\BadMethodCallException $e) { assert($e->getCode() == pq\Exception::BAD_METHODCALL, $e->getCode()."!=".pq\Exception::BAD_METHODCALL); } ?> DONE --EXPECT-- Test DONE --TEST-- sql exception --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); try { $r = $c->exec("SELECT 1 FROM probably_non_existent_table"); } catch (pq\Exception $e) { var_dump($e instanceof pq\Exception\DomainException); var_dump($e->getCode() == pq\Exception::SQL); var_dump($e->sqlstate); } ?> DONE --EXPECT-- Test bool(true) bool(true) string(5) "42P01" DONE --TEST-- fetch type --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); $r = $c->exec("SELECT a,b, NULL as c from generate_series(1,2) a, generate_series(2,4) b"); $r->fetchType = pq\Result::FETCH_ARRAY; foreach ($r as $k => $v) { printf("%s => %s,%s,%s\n", $k, $v[0], $v[1], $v[2]); $r->fetchType = (string) $r->fetchType; } $r->fetchType = pq\Result::FETCH_ASSOC; foreach ($r as $k => $v) { printf("%s => %s,%s,%s\n", $k, $v["a"], $v["b"], $v["c"]); $r->fetchType = (string) $r->fetchType; } $r->fetchType = pq\Result::FETCH_OBJECT; foreach ($r as $k => $v) { printf("%s => %s,%s,%s\n", $k, $v->a, $v->b, $v->c); $r->fetchType = (string) $r->fetchType; } ?> DONE --EXPECT-- Test 0 => 1,2, 1 => 1,3, 2 => 1,4, 3 => 2,2, 4 => 2,3, 5 => 2,4, 0 => 1,2, 1 => 1,3, 2 => 1,4, 3 => 2,2, 4 => 2,3, 5 => 2,4, 0 => 1,2, 1 => 1,3, 2 => 1,4, 3 => 2,2, 4 => 2,3, 5 => 2,4, DONE --TEST-- connection info --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); printf("%s%s%s%s%s%s\n", $c->db, $c->user, $c->pass, $c->host, $c->port, $c->options ); ?> DONE --EXPECTF-- Test %s DONE --TEST-- ext info --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; $e = new ReflectionExtension("pq"); $e->info(); ?> Done --EXPECTF-- Test pq PQ Support => enabled Extension Version => %s Used Library => Version libpq => %s Done --TEST-- large objects --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); $t = $c->startTransaction(); $lob = $t->createLOB(); var_dump($lob->transaction === $t); $lob->write(file_get_contents(__FILE__)); var_dump($lob->tell()); $lob->seek(0, SEEK_SET); $dat = $lob->read(filesize(__FILE__)); var_dump(hash("md5", $dat)==hash_file("md5", __FILE__)); $lob->truncate(5); $lob = new pq\Lob($t, $lob->oid); var_dump($lob->read(123)); $t->commit(); $t->unlinkLOB($lob->oid); ?> DONE --EXPECTF-- Test bool(true) int(489) bool(true) string(5) "%c?php" DONE --TEST-- large object stream --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); $t = $c->startTransaction(); $lob = $t->createLOB(); fwrite($lob->stream, file_get_contents(__FILE__)); var_dump(ftell($lob->stream)); fseek($lob->stream, 0, SEEK_SET); $dat = fread($lob->stream, filesize(__FILE__)); var_dump(hash("md5", $dat)==hash_file("md5", __FILE__)); ftruncate($lob->stream, 5); $lob = new pq\Lob($t, $lob->oid); var_dump(fread($lob->stream, 123)); $t->commit(); $t->unlinkLOB($lob->oid); ?> DONE --EXPECTF-- Test int(503) bool(true) Warning: ftruncate(): Can't truncate this stream! in %s on line %d string(123) "<?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); $t = $c->startTransaction(); $lob = $t->creat" DONE --TEST-- large object closing stream --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); $t = $c->startTransaction(); $lob = $t->createLOB(); var_dump($lob->stream); var_dump($lob->stream); fclose($lob->stream); // bad boy! var_dump($lob->stream); var_dump(fread($lob->stream, 5)); $lob = null; ?> DONE --EXPECTF-- Test resource(%d) of type (stream) resource(%d) of type (stream) Warning: fclose(): %d is not a valid stream resource in %s on line %d resource(%d) of type (stream) string(0) "" DONE --TEST-- large object import/export --SKIPIF-- <?php include "_skipif.inc"; ?> --CLEANUP-- rm lob004.tmp --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); $t = new pq\Transaction($c); $oid = $t->importLOB(__FILE__); var_dump($oid); $t->exportLOB($oid, "lob004.tmp"); var_dump(hash_file("md5",__FILE__)===hash_file("md5","lob004.tmp")); ?> DONE --EXPECTF-- Test int(%d) bool(true) DONE --TEST-- map result --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); $r = $c->exec("select (ARRAY['one','two','three','four','five','six','seven','eight','nine','ten'])[a] num, ". "round(log(a)::numeric,3) log, round(exp(a)::numeric,3) exp from generate_series(1,10) a"); $r->fetchType = pq\Result::FETCH_OBJECT; var_dump($r->map()); var_dump($r->map() == $r->map(0)); var_dump($r->map() == $r->map(0, array(0,1,2))); $r = $c->exec("select * from generate_series(0,1) a, generate_series(0,1) b, generate_series(0,1) c, generate_series(0,1) d ". "order by a,b,c,d"); $r->fetchType = pq\Result::FETCH_ARRAY; var_dump($r->map(array(0,"b",2), "d")); ?> DONE --EXPECTF-- Test object(stdClass)#%d (10) { ["one"]=> object(stdClass)#%d (3) { ["num"]=> string(3) "one" ["log"]=> string(5) "0.000" ["exp"]=> string(5) "2.718" } ["two"]=> object(stdClass)#%d (3) { ["num"]=> string(3) "two" ["log"]=> string(5) "0.301" ["exp"]=> string(5) "7.389" } ["three"]=> object(stdClass)#%d (3) { ["num"]=> string(5) "three" ["log"]=> string(5) "0.477" ["exp"]=> string(6) "20.086" } ["four"]=> object(stdClass)#%d (3) { ["num"]=> string(4) "four" ["log"]=> string(5) "0.602" ["exp"]=> string(6) "54.598" } ["five"]=> object(stdClass)#%d (3) { ["num"]=> string(4) "five" ["log"]=> string(5) "0.699" ["exp"]=> string(7) "148.413" } ["six"]=> object(stdClass)#%d (3) { ["num"]=> string(3) "six" ["log"]=> string(5) "0.778" ["exp"]=> string(7) "403.429" } ["seven"]=> object(stdClass)#%d (3) { ["num"]=> string(5) "seven" ["log"]=> string(5) "0.845" ["exp"]=> string(8) "1096.633" } ["eight"]=> object(stdClass)#%d (3) { ["num"]=> string(5) "eight" ["log"]=> string(5) "0.903" ["exp"]=> string(8) "2980.958" } ["nine"]=> object(stdClass)#%d (3) { ["num"]=> string(4) "nine" ["log"]=> string(5) "0.954" ["exp"]=> string(8) "8103.084" } ["ten"]=> object(stdClass)#%d (3) { ["num"]=> string(3) "ten" ["log"]=> string(5) "1.000" ["exp"]=> string(9) "22026.466" } } bool(true) bool(true) array(2) { [0]=> array(2) { [0]=> array(2) { [0]=> array(1) { [3]=> string(1) "1" } [1]=> array(1) { [3]=> string(1) "1" } } [1]=> array(2) { [0]=> array(1) { [3]=> string(1) "1" } [1]=> array(1) { [3]=> string(1) "1" } } } [1]=> array(2) { [0]=> array(2) { [0]=> array(1) { [3]=> string(1) "1" } [1]=> array(1) { [3]=> string(1) "1" } } [1]=> array(2) { [0]=> array(1) { [3]=> string(1) "1" } [1]=> array(1) { [3]=> string(1) "1" } } } } DONE --TEST-- notify --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $consumer = new pq\Connection(PQ_DSN); $consumer->listen("test", function($channel, $message, $pid) { printf("%s(%d): %s\n", $channel, $pid, $message); }); $producer = new pq\Connection(PQ_DSN); $producer->notify("test", "this is a test"); $consumer->exec("select 1"); $producer->notify("test", "this is an async test"); $r = array($consumer->socket); $w = null; $e = null; stream_select($r, $w, $e, NULL); $consumer->poll(); $producer->notify("other", "this should not show up"); stream_select($r, $w, $e, 0,1000); $consumer->poll(); $producer->notify("test", "just to be sure"); $r = array($consumer->socket); $w = null; $e = null; stream_select($r, $w, $e, 0,1000); $consumer->poll(); $consumer->unlisten("test"); $producer->notify("test", "this shouldn't show up either"); $r = array($consumer->socket); $w = null; $e = null; stream_select($r, $w, $e, 0,1000); $consumer->poll(); ?> DONE --EXPECTF-- Test test(%d): this is a test test(%d): this is an async test test(%d): just to be sure DONE --TEST-- persistent handles --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; for ($i=0; $i<100; ++$i) { $c = new pq\Connection(PQ_DSN, pq\Connection::PERSISTENT); if ($i % 2) { $t = new pq\Transaction($c); $c->listen("chan", function($chan, $msg) { // dummy }); $c->on(pq\Connection::EVENT_RESULT, function($c, $res) { }); } if (!($i%10)) gc_collect_cycles(); $c->exec(""); } var_dump(raphf\stat_persistent_handles()->{"pq\\Connection"}); ?> DONE --EXPECTF-- Test array(1) { ["%S"]=> array(2) { ["used"]=> int(1) ["free"]=> int(2) } } DONE --TEST-- empty result --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; set_error_handler(function($code, $error, $file, $line) { printf("\nWarning: %s in %s on line %d\n", $error, $file, $line); return true; }, E_RECOVERABLE_ERROR); class r extends pq\Result { public $dummy = 2; } var_dump(new pq\Result, get_object_vars(new r)); ?> Done --EXPECTF-- Test Warning: pq\Result not initialized in %s on line %d Warning: pq\Result not initialized in %s on line %d Warning: pq\Result not initialized in %s on line %d Warning: pq\Result not initialized in %s on line %d Warning: pq\Result not initialized in %s on line %d Warning: pq\Result not initialized in %s on line %d Warning: pq\Result not initialized in %s on line %d Warning: pq\Result not initialized in %s on line %d Warning: pq\Result not initialized in %s on line %d Warning: pq\Result not initialized in %s on line %d Warning: pq\Result not initialized in %s on line %d Warning: pq\Result not initialized in %s on line %d Warning: pq\Result not initialized in %s on line %d Warning: pq\Result not initialized in %s on line %d Warning: pq\Result not initialized in %s on line %d Warning: pq\Result not initialized in %s on line %d Warning: pq\Result not initialized in %s on line %d object(pq\Result)#%d (8) { ["status"]=> NULL ["statusMessage"]=> NULL ["errorMessage"]=> NULL ["numRows"]=> int(0) ["numCols"]=> int(0) ["affectedRows"]=> int(0) ["fetchType"]=> int(0) ["autoConvert"]=> int(65535) } array(9) { ["dummy"]=> int(2) ["status"]=> NULL ["statusMessage"]=> NULL ["errorMessage"]=> NULL ["numRows"]=> int(0) ["numCols"]=> int(0) ["affectedRows"]=> int(0) ["fetchType"]=> int(0) ["autoConvert"]=> int(65535) } Done --TEST-- connection reset --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); $c->reset(); var_dump($c->status); $c->on(pq\Connection::EVENT_RESET, function ($c) { print "RESET!\n"; }); $c->reset(); var_dump($c->status); ?> DONE --EXPECT-- Test int(0) RESET! int(0) DONE --TEST-- savepoints --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); $t = $c->startTransaction(); $t->savepoint(); $t->savepoint(); $t->rollback(); $t->commit(); $t->rollback(); ?> DONE --EXPECT-- Test DONE --TEST-- statement w/ bound vars --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); $s = new pq\Statement($c, "bound1", "SELECT \$1::text, \$2::text, \$3::text"); $s->bind(0, $_1); $s->bind(1, $_2); $s->bind(2, $_3); $r = $s->exec(); var_dump($r->fetchAll()); $_1 = "\$1"; $_2 = "\$2"; $_3 = "\$3"; $r = $s->exec(); var_dump($r->fetchAll()); ?> Done --EXPECT-- Test array(1) { [0]=> array(3) { [0]=> NULL [1]=> NULL [2]=> NULL } } array(1) { [0]=> array(3) { [0]=> string(2) "$1" [1]=> string(2) "$2" [2]=> string(2) "$3" } } Done --TEST-- desc statement --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); $s = $c->prepare("test1", "SELECT NOW() - \$1"); $r = $s->exec(array("2012-12-12 12:12:12")); $d = $s->desc(); printf("%s\n", (new pq\Types($c))[$d[0]]->typname); ?> DONE --EXPECT-- Test timestamptz DONE --TEST-- desc statement --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); $s = new pq\Statement($c, "test1", "SELECT NOW() - \$1"); $r = $s->exec(array("2012-12-12 12:12:12")); $d = $s->desc(); printf("%s\n", (new pq\Types($c))[$d[0]]->typname); ?> DONE --EXPECT-- Test timestamptz DONE --TEST-- transaction --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); $c->exec("DROP TABLE IF EXISTS test CASCADE"); $c->exec("SET client_min_messages TO NOTICE"); $c->on(pq\Connection::EVENT_NOTICE, function($c, $notice) { echo "Got notice: $notice\n"; }); var_dump($c->transactionStatus == pq\Connection::TRANS_IDLE); $t = new pq\Transaction($c); var_dump($t->connection->transactionStatus == pq\Connection::TRANS_INTRANS); $c->exec("DROP TABLE IF EXISTS test"); $c->off(pq\Connection::EVENT_NOTICE); $c->exec("CREATE TABLE test (id serial, data text)"); $s = $c->prepare("test_insert", "INSERT INTO test (data) VALUES (\$1)", array((new pq\Types($c))["text"]->oid)); $s->exec(array("a")); $s->exec(array("b")); $s->exec(array("c")); $r = $c->exec("SELECT * FROM test"); while ($row = $r->fetchRow(pq\Result::FETCH_OBJECT)) { printf("%d => %s\n", $row->id, $row->data); } $t->rollback(); var_dump($c->transactionStatus == pq\Connection::TRANS_IDLE); ?> DONE --EXPECT-- Test bool(true) bool(true) Got notice: NOTICE: table "test" does not exist, skipping 1 => a 2 => b 3 => c bool(true) DONE --TEST-- txn properties --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $t = new pq\Transaction(new pq\Connection(PQ_DSN)); var_dump( $t->isolation, $t->readonly, $t->deferrable ); $t->isolation = pq\Transaction::SERIALIZABLE; $t->readonly = true; $t->deferrable = true; var_dump( $t->isolation, $t->readonly, $t->deferrable ); ?> DONE --EXPECTF-- Test int(0) bool(false) bool(false) int(2) bool(true) bool(true) DONE --TEST-- types functionality --SKIPIF-- <?php include "_skipif.inc"; ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); $t = new pq\Types($c, array("pg_catalog", "public")); var_dump($t->connection === $c); var_dump(isset($t["int4"]), empty($t["int4"])); var_dump(isset($t["whatthahell"]), empty($t["whatthahell"])); var_dump(isset($t[25]), empty($t[25])); var_dump(isset($t[0]), empty($t[0])); ?> DONE --EXPECT-- Test bool(true) bool(true) bool(false) bool(false) bool(true) bool(true) bool(false) bool(false) bool(true) DONE --TEST-- extended type support --SKIPIF-- <?php include "_skipif.inc"; ?> --INI-- date.timezone=UTC --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); $r = $c->exec("SET timezone TO UTC; SELECT NULL as null, true as bool, 1::int2 as int2, 2::int4 as int4, 3::int8 as int8, 1.1::float4 as float4, 2.2::float8 as float8, '2013-01-01'::date as date, 1::abstime as abstime, '2013-01-01 01:01:01'::timestamp as timestamp, '2013-01-01 01:01:01 UTC'::timestamptz as timestamptz, array[array[1,2,3],array[4,5,6],array[NULL::int,NULL::int,NULL::int]] as intarray "); var_dump($r->fetchRow(pq\Result::FETCH_ASSOC)); ?> DONE --EXPECTF-- Test array(12) { ["null"]=> NULL ["bool"]=> bool(true) ["int2"]=> int(1) ["int4"]=> int(2) ["int8"]=> int(3) ["float4"]=> float(1.1) ["float8"]=> float(2.2) ["date"]=> object(pq\DateTime)#%d (4) { ["format"]=> string(5) "Y-m-d" ["date"]=> string(%d) "2013-01-01 00:00:00%r(\.000000)?%r" ["timezone_type"]=> int(3) ["timezone"]=> string(3) "UTC" } ["abstime"]=> object(pq\DateTime)#%d (4) { ["format"]=> string(11) "Y-m-d H:i:s" ["date"]=> string(%d) "1970-01-01 00:00:01%r(\.000000)?%r" ["timezone_type"]=> int(1) ["timezone"]=> string(6) "+00:00" } ["timestamp"]=> object(pq\DateTime)#%d (4) { ["format"]=> string(13) "Y-m-d H:i:s.u" ["date"]=> string(%d) "2013-01-01 01:01:01%r(\.000000)?%r" ["timezone_type"]=> int(3) ["timezone"]=> string(3) "UTC" } ["timestamptz"]=> object(pq\DateTime)#%d (4) { ["format"]=> string(14) "Y-m-d H:i:s.uO" ["date"]=> string(%d) "2013-01-01 01:01:01%r(\.000000)?%r" ["timezone_type"]=> int(1) ["timezone"]=> string(6) "+00:00" } ["intarray"]=> array(3) { [0]=> array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) } [1]=> array(3) { [0]=> int(4) [1]=> int(5) [2]=> int(6) } [2]=> array(3) { [0]=> NULL [1]=> NULL [2]=> NULL } } } DONE--TEST-- unbuffered result --SKIPIF-- <?php include "_skipif.inc"; defined("pq\\Result::SINGLE_TUPLE") or die("skip need pq\\Result::SINGLE_TUPLE"); ?> --FILE-- <?php echo "Test\n"; include "_setup.inc"; $c = new pq\Connection(PQ_DSN); var_dump($c->unbuffered ? true : false); $c->unbuffered = 1; var_dump($c->unbuffered); $c->execAsync("SELECT a from generate_series(1,10) a", function($res) { switch ($res->status) { case pq\Result::SINGLE_TUPLE: $res->fetchCol($val, "a"); printf("%s\n", $val); break; case pq\Result::TUPLES_OK: printf("-> fetching done\n"); break; default: printf("!! %s\n", $res->errorMessage); break; } }); do { while ($c->busy) { $r = array($c->socket); $w = $e = null; if (stream_select($r, $w, $e, null)) { $c->poll(); } } } while ($c->getResult()); ?> DONE --EXPECTF-- Test bool(false) bool(true) 1 2 3 4 5 6 7 8 9 10 -> fetching done DONE <?php define("PQ_DSN", "host=localhost"); <?php function _ext($ext) { extension_loaded($ext) or die("skip $ext not loaded"); } _ext("pq"); include "_setup.inc"; defined("PQ_DSN") or die("skip PQ_DSN undefined"); try { new pq\Connection(PQ_DSN); } catch (pq\Exception $e) { die("skip could not connect to PQ_DSN ".$e->getMessage()); } UdMg(~82[���GBMB