DB2Statement.php 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. <?php
  2. /*
  3. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  4. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  5. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  6. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  7. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  8. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  9. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  10. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  11. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  12. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  13. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  14. *
  15. * This software consists of voluntary contributions made by many individuals
  16. * and is licensed under the MIT license. For more information, see
  17. * <http://www.doctrine-project.org>.
  18. */
  19. namespace Doctrine\DBAL\Driver\IBMDB2;
  20. use Doctrine\DBAL\Driver\Statement;
  21. class DB2Statement implements \IteratorAggregate, Statement
  22. {
  23. /**
  24. * @var resource
  25. */
  26. private $_stmt = null;
  27. /**
  28. * @var array
  29. */
  30. private $_bindParam = array();
  31. /**
  32. * @var string Name of the default class to instantiate when fetch mode is \PDO::FETCH_CLASS.
  33. */
  34. private $defaultFetchClass = '\stdClass';
  35. /**
  36. * @var string Constructor arguments for the default class to instantiate when fetch mode is \PDO::FETCH_CLASS.
  37. */
  38. private $defaultFetchClassCtorArgs = array();
  39. /**
  40. * @var integer
  41. */
  42. private $_defaultFetchMode = \PDO::FETCH_BOTH;
  43. /**
  44. * Indicates whether the statement is in the state when fetching results is possible
  45. *
  46. * @var bool
  47. */
  48. private $result = false;
  49. /**
  50. * DB2_BINARY, DB2_CHAR, DB2_DOUBLE, or DB2_LONG
  51. *
  52. * @var array
  53. */
  54. static private $_typeMap = array(
  55. \PDO::PARAM_INT => DB2_LONG,
  56. \PDO::PARAM_STR => DB2_CHAR,
  57. );
  58. /**
  59. * @param resource $stmt
  60. */
  61. public function __construct($stmt)
  62. {
  63. $this->_stmt = $stmt;
  64. }
  65. /**
  66. * {@inheritdoc}
  67. */
  68. public function bindValue($param, $value, $type = null)
  69. {
  70. return $this->bindParam($param, $value, $type);
  71. }
  72. /**
  73. * {@inheritdoc}
  74. */
  75. public function bindParam($column, &$variable, $type = null, $length = null)
  76. {
  77. $this->_bindParam[$column] =& $variable;
  78. if ($type && isset(self::$_typeMap[$type])) {
  79. $type = self::$_typeMap[$type];
  80. } else {
  81. $type = DB2_CHAR;
  82. }
  83. if (!db2_bind_param($this->_stmt, $column, "variable", DB2_PARAM_IN, $type)) {
  84. throw new DB2Exception(db2_stmt_errormsg());
  85. }
  86. return true;
  87. }
  88. /**
  89. * {@inheritdoc}
  90. */
  91. public function closeCursor()
  92. {
  93. if ( ! $this->_stmt) {
  94. return false;
  95. }
  96. $this->_bindParam = array();
  97. if (!db2_free_result($this->_stmt)) {
  98. return false;
  99. }
  100. $this->result = false;
  101. return true;
  102. }
  103. /**
  104. * {@inheritdoc}
  105. */
  106. public function columnCount()
  107. {
  108. if ( ! $this->_stmt) {
  109. return false;
  110. }
  111. return db2_num_fields($this->_stmt);
  112. }
  113. /**
  114. * {@inheritdoc}
  115. */
  116. public function errorCode()
  117. {
  118. return db2_stmt_error();
  119. }
  120. /**
  121. * {@inheritdoc}
  122. */
  123. public function errorInfo()
  124. {
  125. return array(
  126. 0 => db2_stmt_errormsg(),
  127. 1 => db2_stmt_error(),
  128. );
  129. }
  130. /**
  131. * {@inheritdoc}
  132. */
  133. public function execute($params = null)
  134. {
  135. if ( ! $this->_stmt) {
  136. return false;
  137. }
  138. if ($params === null) {
  139. ksort($this->_bindParam);
  140. $params = array();
  141. foreach ($this->_bindParam as $column => $value) {
  142. $params[] = $value;
  143. }
  144. }
  145. $retval = @db2_execute($this->_stmt, $params);
  146. if ($retval === false) {
  147. throw new DB2Exception(db2_stmt_errormsg());
  148. }
  149. $this->result = true;
  150. return $retval;
  151. }
  152. /**
  153. * {@inheritdoc}
  154. */
  155. public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null)
  156. {
  157. $this->_defaultFetchMode = $fetchMode;
  158. $this->defaultFetchClass = $arg2 ? $arg2 : $this->defaultFetchClass;
  159. $this->defaultFetchClassCtorArgs = $arg3 ? (array) $arg3 : $this->defaultFetchClassCtorArgs;
  160. return true;
  161. }
  162. /**
  163. * {@inheritdoc}
  164. */
  165. public function getIterator()
  166. {
  167. $data = $this->fetchAll();
  168. return new \ArrayIterator($data);
  169. }
  170. /**
  171. * {@inheritdoc}
  172. */
  173. public function fetch($fetchMode = null)
  174. {
  175. // do not try fetching from the statement if it's not expected to contain result
  176. // in order to prevent exceptional situation
  177. if (!$this->result) {
  178. return false;
  179. }
  180. $fetchMode = $fetchMode ?: $this->_defaultFetchMode;
  181. switch ($fetchMode) {
  182. case \PDO::FETCH_BOTH:
  183. return db2_fetch_both($this->_stmt);
  184. case \PDO::FETCH_ASSOC:
  185. return db2_fetch_assoc($this->_stmt);
  186. case \PDO::FETCH_CLASS:
  187. $className = $this->defaultFetchClass;
  188. $ctorArgs = $this->defaultFetchClassCtorArgs;
  189. if (func_num_args() >= 2) {
  190. $args = func_get_args();
  191. $className = $args[1];
  192. $ctorArgs = isset($args[2]) ? $args[2] : array();
  193. }
  194. $result = db2_fetch_object($this->_stmt);
  195. if ($result instanceof \stdClass) {
  196. $result = $this->castObject($result, $className, $ctorArgs);
  197. }
  198. return $result;
  199. case \PDO::FETCH_NUM:
  200. return db2_fetch_array($this->_stmt);
  201. case \PDO::FETCH_OBJ:
  202. return db2_fetch_object($this->_stmt);
  203. default:
  204. throw new DB2Exception("Given Fetch-Style " . $fetchMode . " is not supported.");
  205. }
  206. }
  207. /**
  208. * {@inheritdoc}
  209. */
  210. public function fetchAll($fetchMode = null)
  211. {
  212. $rows = array();
  213. switch ($fetchMode) {
  214. case \PDO::FETCH_CLASS:
  215. while ($row = call_user_func_array(array($this, 'fetch'), func_get_args())) {
  216. $rows[] = $row;
  217. }
  218. break;
  219. case \PDO::FETCH_COLUMN:
  220. while ($row = $this->fetchColumn()) {
  221. $rows[] = $row;
  222. }
  223. break;
  224. default:
  225. while ($row = $this->fetch($fetchMode)) {
  226. $rows[] = $row;
  227. }
  228. }
  229. return $rows;
  230. }
  231. /**
  232. * {@inheritdoc}
  233. */
  234. public function fetchColumn($columnIndex = 0)
  235. {
  236. $row = $this->fetch(\PDO::FETCH_NUM);
  237. if (false === $row) {
  238. return false;
  239. }
  240. return isset($row[$columnIndex]) ? $row[$columnIndex] : null;
  241. }
  242. /**
  243. * {@inheritdoc}
  244. */
  245. public function rowCount()
  246. {
  247. return (@db2_num_rows($this->_stmt))?:0;
  248. }
  249. /**
  250. * Casts a stdClass object to the given class name mapping its' properties.
  251. *
  252. * @param \stdClass $sourceObject Object to cast from.
  253. * @param string|object $destinationClass Name of the class or class instance to cast to.
  254. * @param array $ctorArgs Arguments to use for constructing the destination class instance.
  255. *
  256. * @return object
  257. *
  258. * @throws DB2Exception
  259. */
  260. private function castObject(\stdClass $sourceObject, $destinationClass, array $ctorArgs = array())
  261. {
  262. if ( ! is_string($destinationClass)) {
  263. if ( ! is_object($destinationClass)) {
  264. throw new DB2Exception(sprintf(
  265. 'Destination class has to be of type string or object, %s given.', gettype($destinationClass)
  266. ));
  267. }
  268. } else {
  269. $destinationClass = new \ReflectionClass($destinationClass);
  270. $destinationClass = $destinationClass->newInstanceArgs($ctorArgs);
  271. }
  272. $sourceReflection = new \ReflectionObject($sourceObject);
  273. $destinationClassReflection = new \ReflectionObject($destinationClass);
  274. /** @var \ReflectionProperty[] $destinationProperties */
  275. $destinationProperties = array_change_key_case($destinationClassReflection->getProperties(), \CASE_LOWER);
  276. foreach ($sourceReflection->getProperties() as $sourceProperty) {
  277. $sourceProperty->setAccessible(true);
  278. $name = $sourceProperty->getName();
  279. $value = $sourceProperty->getValue($sourceObject);
  280. // Try to find a case-matching property.
  281. if ($destinationClassReflection->hasProperty($name)) {
  282. $destinationProperty = $destinationClassReflection->getProperty($name);
  283. $destinationProperty->setAccessible(true);
  284. $destinationProperty->setValue($destinationClass, $value);
  285. continue;
  286. }
  287. $name = strtolower($name);
  288. // Try to find a property without matching case.
  289. // Fallback for the driver returning either all uppercase or all lowercase column names.
  290. if (isset($destinationProperties[$name])) {
  291. $destinationProperty = $destinationProperties[$name];
  292. $destinationProperty->setAccessible(true);
  293. $destinationProperty->setValue($destinationClass, $value);
  294. continue;
  295. }
  296. $destinationClass->$name = $value;
  297. }
  298. return $destinationClass;
  299. }
  300. }