Edit via SFTP
  1. <?php namespace ferret\data;
  2. /*
  3.   PURPOSE: wrapper class for mysqli library
  4.   HISTORY:
  5.   2019-09-23 started adapting from db-conn-mysqli.php
  6. */
  7.  
  8. class cMySQL extends cDatabaseSQL {
  9. #use tNativeObject;
  10. use tStandardSpec;
  11. #use tErrorHandling;
  12.  
  13. // ++ ACTIONS ++ //
  14.  
  15. private $oAction;
  16.  
  17. public function Open() {
  18. $oSpec = $this->GetSpec();
  19. $oNative = new \mysqli(
  20. $oSpec->GetHostString(),
  21. $oSpec->GetUserString(),
  22. $oSpec->GetPassString(),
  23. $oSpec->GetSchemaString()
  24. ); // open the connection natively
  25. if (is_object($oNative)) {
  26. $this->Action()->SetOkay(TRUE);
  27. $this->SetIt($oNative);
  28. } else {
  29. $this->Action()->SetOkay(FALSE);
  30. }
  31. #$this->SetNativeObject($oNative); // save native object
  32. }
  33. public function Shut() { $this->GetNativeObject()->close(); }
  34.  
  35. // -- ACTIONS -- //
  36. // ++ ERROR HANDLING ++ //
  37.  
  38. // CEMENT: tErrorHandling
  39. public function ErrorNumber() { return $this->GetNativeObject()->errno; }
  40. // CEMENT: tErrorHandling
  41. public function ErrorString() { return $this->GetNativeObject()->error; }
  42.  
  43. // -- ERROR HANDLING -- //
  44. // ++ OBJECTS ++ //
  45.  
  46. protected function GetNativeObject() {
  47. if ($this->Action()->GetOkay()) {
  48. return $this->GetIt();
  49. } else {
  50. $s = 'Ferreteria internal error: attempting to retrieve DB object when it is unavailable.';
  51. $e = new \ferret\except\cInternal($s);
  52. $e->AddDiagnostic(cEnv::BoldIt('host') .': '.$this->GetHostString);
  53. $e->AddDiagnostic(cEnv::BoldIt('db') .': '.$this->GetSchemaString);
  54. $e->AddDiagnostic(cEnv::BoldIt('user') .': '.$this->GetUserString);
  55. throw $e;
  56. }
  57. }
  58.  
  59. // -- OBJECTS -- //
  60. // ++ SANITIZING ++ //
  61.  
  62. public function SanitizeString(string $sSQL) {
  63. if (is_array($sSQL)) {
  64. throw new exception('Ferreteria usage error: received array to sanitize, expected scalar.');
  65. }
  66. return $this->GetNativeObject()->escape_string($sSQL);
  67. }
  68.  
  69. // -- SANITIZING -- //
  70. // ++ DATA R/W ++ //
  71.  
  72. // CEMENT: cDatabase
  73. public function ExecuteAction(string $sql) {
  74. $this->sql = $sql;
  75. return $this->GetNativeObject()->query($sql);
  76. }
  77. // CEMENT: cDatabase
  78. public function CountOfAffectedRows() : int {
  79. return $this->GetNativeObject()->affected_rows;
  80. }
  81. /*----
  82.   CEMENT: cDatabase
  83.   HISTORY:
  84.   2020-01-13 reworked to match changes to ProcessResultset()
  85.   */
  86. public function FetchRecordset(string $sql,cTabloid $tbl) : cSelectResult {
  87. global $sDangerStatus;
  88.  
  89. $sDangerStatus = "Executing -<ul><li>SQL: $sql<li>TABLE CLASS: ".get_class($tbl).'</ul>';
  90. @$poRes = $this->GetNativeObject()->query($sql); // returns a mysqli_result if successful
  91.  
  92. $sDangerStatus = NULL;
  93. return $this->ProcessResultset($poRes,$tbl,$sql);
  94. }
  95. /*----
  96.   HISTORY:
  97.   2021-04-18 note on SetValues_fromStorage() says: is this still even a thing?
  98.   2021-06-01 Established that SetValues_fromStorage() is the correct thing to use.
  99.   At the base of the hierarchy, it doesn't know about the Storage Bank layer.
  100.   Descendant classes will override it to provide that behavior.
  101.   */
  102. public function FetchNextResultRow(cMultiRowSerial $rs) : cRecordResult {
  103. $oNative = $rs->GetDriverBlob();
  104. $ar = $oNative->fetch_assoc();
  105. $os = new cRecordResult();
  106. if (is_null($ar)) {
  107. $os->ClearIt();
  108. } else {
  109. $rc = $rs->SpawnRow();
  110. $os->SetIt($rc);
  111.  
  112. #echo 'SETTING VALUES for RC in class '.get_class($rc).': '.\fcArray::Render($ar);
  113.  
  114. $rc->SetValues_fromStorage($ar);
  115.  
  116. #echo 'VALUES as retrieved from RC:'.$rc->Dump();
  117. }
  118. return $os;
  119. }
  120. protected function IsResultOk($poRes) : bool { return is_object($poRes); }
  121.  
  122. // -- DATA R/W -- //
  123. // ++ RESULTS ACTIONS ++ //
  124.  
  125. public function RewindResultRows(cMultiRowSerial $rs) { return $rs->GetDriverBlob()->data_seek(0); }
  126. // ++ RESULTS CALCULATIONS ++ //
  127.  
  128. // CEMENT: cDatabase
  129. public function CreatedID() { return $this->GetNativeObject()->insert_id; }
  130. // CEMENT: cDatabase
  131. public function CountOfReturnedRows(cMultiRow $rs) : int {
  132. $oNative = $rs->GetDriverBlob();
  133. if (is_null($oNative)) {
  134. return 0;
  135. } else {
  136. return $oNative->num_rows;
  137. }
  138. }
  139.  
  140. // -- RESULTS CALCULATIONS -- //
  141. // ++ TRANSACTIONS ++ //
  142.  
  143. /*
  144.   NOTE: mysqli supports named transactions and other types besides r/w, but I don't know if this is common.
  145. Not supporting it for now, but that might be something to add later (optional arguments) if it is common and useful.
  146.   */
  147.  
  148. // CEMENT: cDatabase
  149. public function TransactionOpen() {
  150. $o = $this->GetNativeObject();
  151. $o->autocommit(FALSE); // turn off autocommit
  152. $o->begin_transaction(MYSQLI_TRANS_START_READ_WRITE);
  153. }
  154. // CEMENT: cDatabase
  155. public function TransactionSave() {
  156. $o = $this->GetNativeObject();
  157. $o->commit(); // commit the transaction
  158. $o->autocommit(TRUE); // turn autocommit back on
  159. }
  160. // CEMENT: cDatabase
  161. public function TransactionKill() {
  162. $o = $this->GetNativeObject();
  163. $o->rollback(); // roll back the transaction
  164. $o->autocommit(TRUE); // turn autocommit back on
  165. }
  166.  
  167. // -- TRANSACTIONS -- //
  168. }
  169.