Subversion Repositories oidplus

Rev

Rev 1233 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. <?php
  2.  
  3. /*
  4.  * OIDplus 2.0
  5.  * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft
  6.  *
  7.  * Licensed under the Apache License, Version 2.0 (the "License");
  8.  * you may not use this file except in compliance with the License.
  9.  * You may obtain a copy of the License at
  10.  *
  11.  *     http://www.apache.org/licenses/LICENSE-2.0
  12.  *
  13.  * Unless required by applicable law or agreed to in writing, software
  14.  * distributed under the License is distributed on an "AS IS" BASIS,
  15.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16.  * See the License for the specific language governing permissions and
  17.  * limitations under the License.
  18.  */
  19.  
  20. namespace ViaThinkSoft\OIDplus;
  21.  
  22. // phpcs:disable PSR1.Files.SideEffects
  23. \defined('INSIDE_OIDPLUS') or die;
  24. // phpcs:enable PSR1.Files.SideEffects
  25.  
  26. class OIDplusDatabaseConnectionSqlSrv extends OIDplusDatabaseConnection {
  27.  
  28.         /**
  29.          * @var mixed|null
  30.          */
  31.         private $conn = null;
  32.  
  33.         /**
  34.          * @var string|null
  35.          */
  36.         private $last_error = null;
  37.  
  38.         /**
  39.          * @param string $sql
  40.          * @param array|null $prepared_args
  41.          * @return OIDplusQueryResultSqlSrv
  42.          * @throws OIDplusException
  43.          */
  44.         public function doQuery(string $sql, array $prepared_args=null): OIDplusQueryResult {
  45.                 $this->last_error = null;
  46.                 try {
  47.                         $res = sqlsrv_query($this->conn, $sql, $prepared_args,
  48.                                 array(
  49.                                         // SQLSRV_CURSOR_FORWARD ('forward', default)
  50.                                         // Lets you move one row at a time starting at the first row of the result set until you reach the end of the result set.
  51.                                         // => Does not work with sqlsrv_num_rows();
  52.  
  53.                                         // SQLSRV_CURSOR_STATIC ('static')
  54.                                         // Lets you access rows in any order but will not reflect changes in the database.
  55.                                         // => Does not work with transaction rollback?! (Testcase failed)
  56.  
  57.                                         // SQLSRV_CURSOR_DYNAMIC
  58.                                         // Lets you access rows in any order and will reflect changes in the database.
  59.                                         // => Does not work with sqlsrv_num_rows();
  60.  
  61.                                         // SQLSRV_CURSOR_KEYSET ('keyset')
  62.                                         // Lets you access rows in any order. However, a keyset cursor does not update the row count if a row is deleted from the table (a deleted row is returned with no values).
  63.                                         // => Does not work with transaction rollback?! (Testcase failed)
  64.  
  65.                                         // SQLSRV_CURSOR_CLIENT_BUFFERED ('buffered')
  66.                                         // Lets you access rows in any order. Creates a client-side cursor query.
  67.                                         // => Seems to work fine
  68.  
  69.                                         'Scrollable' => SQLSRV_CURSOR_CLIENT_BUFFERED
  70.                                 )
  71.                         );
  72.                 } catch (\Exception $e) {
  73.                         $this->last_error = $e->getMessage();
  74.                         throw new OIDplusSQLException($sql, $e->getMessage());
  75.                 }
  76.  
  77.                 if ($res === false) {
  78.                         $this->last_error = print_r(sqlsrv_errors(), true);
  79.                         throw new OIDplusSQLException($sql, $this->error());
  80.                 } else {
  81.                         return new OIDplusQueryResultSqlSrv($res);
  82.                 }
  83.         }
  84.  
  85.  
  86.         /**
  87.          * @return string
  88.          */
  89.         public function error(): string {
  90.                 $err = $this->last_error;
  91.                 if ($err === null) $err = '';
  92.                 return $err;
  93.         }
  94.  
  95.         /**
  96.          * @return string
  97.          */
  98.         private static function get_sqlsrv_dll_name(): string {
  99.                 ob_start();
  100.                 phpinfo(INFO_GENERAL);
  101.                 $x = ob_get_contents();
  102.                 ob_end_clean();
  103.  
  104.                 $architecture =
  105.                         preg_match('@Architecture.+(x86|x64)@', $x, $m) ? $m[1] : '*';
  106.  
  107.                 $threadsafety =
  108.                         preg_match('@Thread Safety.+(enabled|disabled)@', $x, $m)
  109.                         ? ($m[1] == 'enabled' ? 'ts' : 'nts') : '*';
  110.  
  111.                 $m = explode('.',phpversion());
  112.                 $version = $m[0].$m[1];
  113.  
  114.                 // e.g. php_sqlsrv_82_ts_x64.dll
  115.                 return "php_sqlsrv_${version}_${threadsafety}_${architecture}.dll";
  116.         }
  117.  
  118.         /**
  119.          * @return void
  120.          * @throws OIDplusConfigInitializationException
  121.          * @throws OIDplusException
  122.          */
  123.         protected function doConnect()/*: void*/ {
  124.                 // Download here: https://learn.microsoft.com/en-us/sql/connect/php/download-drivers-php-sql-server?view=sql-server-ver16
  125.                 if (!function_exists('sqlsrv_connect')) throw new OIDplusException(_L('PHP extension "%1" not installed',self::get_sqlsrv_dll_name()));
  126.  
  127.                 // Try connecting to the database
  128.                 $servername = OIDplus::baseConfig()->getValue('SQLSRV_SERVER',   'localhost\oidplus,49010');
  129.                 $username   = OIDplus::baseConfig()->getValue('SQLSRV_USERNAME', '');
  130.                 $password   = OIDplus::baseConfig()->getValue('SQLSRV_PASSWORD', '');
  131.                 $database   = OIDplus::baseConfig()->getValue('SQLSRV_DATABASE', 'oidplus');
  132.  
  133.                 $connectionInfo = array(
  134.                         "Database" => $database,
  135.                         "CharacterSet" => 'UTF-8',
  136.                         // TODO: Make addition connection infos configurable
  137.                 );
  138.  
  139.                 if ($username != '') {
  140.                         $connectionInfo['UID'] = $username;
  141.                         $connectionInfo['PWD'] = $password;
  142.                 }
  143.  
  144.                 $this->conn = @sqlsrv_connect($servername, $connectionInfo);
  145.  
  146.                 if (!$this->conn) {
  147.                         $message = print_r(sqlsrv_errors(), true);
  148.                         throw new OIDplusConfigInitializationException(trim(_L('Connection to the database failed!').' '.$message));
  149.                 }
  150.  
  151.                 $this->last_error = null;
  152.         }
  153.  
  154.         /**
  155.          * @return void
  156.          */
  157.         protected function doDisconnect()/*: void*/ {
  158.                 if (!is_null($this->conn)) {
  159.                         sqlsrv_close($this->conn);
  160.                         $this->conn = null;
  161.                 }
  162.         }
  163.  
  164.         /**
  165.          * @var bool
  166.          */
  167.         private $intransaction = false;
  168.  
  169.         /**
  170.          * @return bool
  171.          */
  172.         public function transaction_supported(): bool {
  173.                 return true;
  174.         }
  175.  
  176.         /**
  177.          * @return int
  178.          */
  179.         public function transaction_level(): int {
  180.                 return $this->intransaction ? 1 : 0;
  181.         }
  182.  
  183.         /**
  184.          * @return void
  185.          * @throws OIDplusException
  186.          */
  187.         public function transaction_begin()/*: void*/ {
  188.                 if ($this->intransaction) throw new OIDplusException(_L('Nested transactions are not supported by this database plugin.'));
  189.                 sqlsrv_begin_transaction($this->conn);
  190.                 $this->intransaction = true;
  191.         }
  192.  
  193.         /**
  194.          * @return void
  195.          */
  196.         public function transaction_commit()/*: void*/ {
  197.                 sqlsrv_commit($this->conn);
  198.                 $this->intransaction = false;
  199.         }
  200.  
  201.         /**
  202.          * @return void
  203.          */
  204.         public function transaction_rollback()/*: void*/ {
  205.                 sqlsrv_rollback($this->conn);
  206.                 $this->intransaction = false;
  207.         }
  208.  
  209.         /**
  210.          * @return string
  211.          */
  212.         public function sqlDate(): string {
  213.                 return 'getdate()';
  214.         }
  215.  
  216.         /**
  217.          * @param bool $mustExist
  218.          * @return OIDplusSqlSlangPlugin|null
  219.          * @throws OIDplusConfigInitializationException
  220.          */
  221.         protected function doGetSlang(bool $mustExist=true)/*: ?OIDplusSqlSlangPlugin*/ {
  222.                 $slang = OIDplus::getSqlSlangPlugin('mssql');
  223.                 if (is_null($slang)) {
  224.                         throw new OIDplusConfigInitializationException(_L('SQL-Slang plugin "%1" is missing. Please check if it exists in the directory "plugin/sqlSlang". If it is not existing, please recover it from an SVN snapshot or OIDplus TAR.GZ file.','mssql'));
  225.                 }
  226.                 return $slang;
  227.         }
  228.  
  229.         /**
  230.          * @return array
  231.          */
  232.         public function getExtendedInfo(): array {
  233.                 $servername = OIDplus::baseConfig()->getValue('SQLSRV_SERVER',   'localhost\oidplus,49010');
  234.                 $username   = OIDplus::baseConfig()->getValue('SQLSRV_USERNAME', '');
  235.                 $password   = OIDplus::baseConfig()->getValue('SQLSRV_PASSWORD', '');
  236.                 $database   = OIDplus::baseConfig()->getValue('SQLSRV_DATABASE', 'oidplus');
  237.                 return array(
  238.                         _L('Hostname') => $servername,
  239.                         _L('Username') => $username,
  240.                         _L('Password') => $password != '' ? '('._L('redacted').')' : '',
  241.                         _L('Database') => $database
  242.                 );
  243.         }
  244. }
  245.