]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/database/MysqliManager.php
Release 6.4.0beta1
[Github/sugarcrm.git] / include / database / MysqliManager.php
1 <?php
2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4  * SugarCRM Community Edition is a customer relationship management program developed by
5  * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
6  * 
7  * This program is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU Affero General Public License version 3 as published by the
9  * Free Software Foundation with the addition of the following permission added
10  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13  * 
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
17  * details.
18  * 
19  * You should have received a copy of the GNU Affero General Public License along with
20  * this program; if not, see http://www.gnu.org/licenses or write to the Free
21  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301 USA.
23  * 
24  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
26  * 
27  * The interactive user interfaces in modified source and object code versions
28  * of this program must display Appropriate Legal Notices, as required under
29  * Section 5 of the GNU Affero General Public License version 3.
30  * 
31  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32  * these Appropriate Legal Notices must retain the display of the "Powered by
33  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34  * technical reasons, the Appropriate Legal Notices must display the words
35  * "Powered by SugarCRM".
36  ********************************************************************************/
37
38 /*********************************************************************************
39
40 * Description: This file handles the Data base functionality for the application.
41 * It acts as the DB abstraction layer for the application. It depends on helper classes
42 * which generate the necessary SQL. This sql is then passed to PEAR DB classes.
43 * The helper class is chosen in DBManagerFactory, which is driven by 'db_type' in 'dbconfig' under config.php.
44 *
45 * All the functions in this class will work with any bean which implements the meta interface.
46 * The passed bean is passed to helper class which uses these functions to generate correct sql.
47 *
48 * The meta interface has the following functions:
49 * getTableName()                Returns table name of the object.
50 * getFieldDefinitions()         Returns a collection of field definitions in order.
51 * getFieldDefintion(name)       Return field definition for the field.
52 * getFieldValue(name)           Returns the value of the field identified by name.
53 *                               If the field is not set, the function will return boolean FALSE.
54 * getPrimaryFieldDefinition()   Returns the field definition for primary key
55 *
56 * The field definition is an array with the following keys:
57 *
58 * name      This represents name of the field. This is a required field.
59 * type      This represents type of the field. This is a required field and valid values are:
60 *           �   int
61 *           �   long
62 *           �   varchar
63 *           �   text
64 *           �   date
65 *           �   datetime
66 *           �   double
67 *           �   float
68 *           �   uint
69 *           �   ulong
70 *           �   time
71 *           �   short
72 *           �   enum
73 * length    This is used only when the type is varchar and denotes the length of the string.
74 *           The max value is 255.
75 * enumvals  This is a list of valid values for an enum separated by "|".
76 *           It is used only if the type is �enum�;
77 * required  This field dictates whether it is a required value.
78 *           The default value is �FALSE�.
79 * isPrimary This field identifies the primary key of the table.
80 *           If none of the fields have this flag set to �TRUE�,
81 *           the first field definition is assume to be the primary key.
82 *           Default value for this field is �FALSE�.
83 * default   This field sets the default value for the field definition.
84 *
85 *
86 * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc.
87 * All Rights Reserved.
88 * Contributor(s): ______________________________________..
89 ********************************************************************************/
90
91 require_once('include/database/MysqlManager.php');
92
93 /**
94  * MySQL manager implementation for mysqli extension
95  */
96 class MysqliManager extends MysqlManager
97 {
98         /**
99          * @see DBManager::$dbType
100          */
101         public $dbType = 'mysql';
102         public $variant = 'mysqli';
103         public $priority = 10;
104         public $label = 'LBL_MYSQLI';
105
106         /**
107          * @see DBManager::$backendFunctions
108          */
109         protected $backendFunctions = array(
110                 'free_result'        => 'mysqli_free_result',
111                 'close'              => 'mysqli_close',
112                 'row_count'          => 'mysqli_num_rows',
113                 'affected_row_count' => 'mysqli_affected_rows',
114                 );
115
116         /**
117          * @see MysqlManager::query()
118          */
119         public function query($sql, $dieOnError = false, $msg = '', $suppress = false, $keepResult = false)
120         {
121                 if(is_array($sql)) {
122                         return $this->queryArray($sql, $dieOnError, $msg, $suppress);
123                 }
124                 static $queryMD5 = array();
125
126                 parent::countQuery($sql);
127                 $GLOBALS['log']->info('Query:' . $sql);
128                 $this->checkConnection();
129                 $this->query_time = microtime(true);
130                 $this->lastsql = $sql;
131                 $result = $suppress?@mysqli_query($this->database,$sql):mysqli_query($this->database,$sql);
132                 $md5 = md5($sql);
133
134                 if (empty($queryMD5[$md5]))
135                         $queryMD5[$md5] = true;
136
137                 $this->query_time = microtime(true) - $this->query_time;
138                 $GLOBALS['log']->info('Query Execution Time:'.$this->query_time);
139
140                 // This is some heavy duty debugging, leave commented out unless you need this:
141                 /*
142                 $bt = debug_backtrace();
143                 for ( $i = count($bt) ; $i-- ; $i > 0 ) {
144                         if ( strpos('MysqliManager.php',$bt[$i]['file']) === false ) {
145                                 $line = $bt[$i];
146                         }
147                 }
148
149                 $GLOBALS['log']->fatal("${line['file']}:${line['line']} ${line['function']} \nQuery: $sql\n");
150                 */
151
152
153                 if($keepResult)
154                         $this->lastResult = $result;
155                 $this->checkError($msg.' Query Failed: ' . $sql, $dieOnError);
156
157                 return $result;
158         }
159
160         /**
161          * Returns the number of rows affected by the last query
162          *
163          * @return int
164          */
165         public function getAffectedRowCount($result)
166         {
167                 return mysqli_affected_rows($this->getDatabase());
168         }
169
170         /**
171          * Disconnects from the database
172          *
173          * Also handles any cleanup needed
174          */
175         public function disconnect()
176         {
177                 $GLOBALS['log']->debug('Calling MySQLi::disconnect()');
178                 if(!empty($this->database)){
179                         $this->freeResult();
180                         mysqli_close($this->database);
181                         $this->database = null;
182                 }
183         }
184
185         /**
186          * @see DBManager::freeDbResult()
187          */
188         protected function freeDbResult($dbResult)
189         {
190                 if(!empty($dbResult))
191                         mysqli_free_result($dbResult);
192         }
193
194         /**
195          * Returns the number of rows returned by the result
196          *
197          * @param  resource $result
198          * @return int
199          */
200         public function getRowCount($result)
201         {
202                 if(!empty($result)) {
203                         return mysqli_num_rows($result);
204                 }
205                 return 0;
206         }
207
208         /**
209          * @see DBManager::getFieldsArray()
210          */
211         public function getFieldsArray($result, $make_lower_case = false)
212         {
213                 $field_array = array();
214
215                 if (!isset($result) || empty($result))
216                         return 0;
217
218                 $i = 0;
219                 while ($i < mysqli_num_fields($result)) {
220                         $meta = mysqli_fetch_field_direct($result, $i);
221                         if (!$meta)
222                                 return 0;
223
224                         if($make_lower_case == true)
225                                 $meta->name = strtolower($meta->name);
226
227                         $field_array[] = $meta->name;
228
229                         $i++;
230                 }
231
232                 return $field_array;
233         }
234
235         /**
236          * @see DBManager::fetchByAssoc()
237          */
238         public function fetchByAssoc($result, $rowNum = -1, $encode = true)
239         {
240                 if (!$result)
241                         return false;
242
243                 if ($result && $rowNum > -1) {
244                         if ($this->getRowCount($result) > $rowNum)
245                                 mysqli_data_seek($result, $rowNum);
246                 }
247
248                 $row = mysqli_fetch_assoc($result);
249
250                 if ($encode && $this->encode && is_array($row))
251                         return array_map('to_html', $row);
252
253                 if($row == null) $row = false; //Make sure MySQLi driver results are consistent with other database drivers
254                 return $row;
255         }
256
257         /**
258          * @see DBManager::quote()
259          */
260         public function quote($string)
261         {
262                 return mysqli_real_escape_string($this->getDatabase(),$this->quoteInternal($string));
263         }
264
265         /**
266          * @see DBManager::connect()
267          */
268         public function connect(array $configOptions = null, $dieOnError = false)
269         {
270                 global $sugar_config;
271
272                 if (is_null($configOptions))
273                         $configOptions = $sugar_config['dbconfig'];
274
275                 if(!isset($this->database)) {
276
277                         //mysqli connector has a separate parameter for port.. We need to separate it out from the host name
278                         $dbhost=$configOptions['db_host_name'];
279                         $dbport=null;
280                         $pos=strpos($configOptions['db_host_name'],':');
281                         if ($pos !== false) {
282                                 $dbhost=substr($configOptions['db_host_name'],0,$pos);
283                                 $dbport=substr($configOptions['db_host_name'],$pos+1);
284                         }
285
286                         $this->database = mysqli_connect($dbhost,$configOptions['db_user_name'],$configOptions['db_password'],isset($configOptions['db_name'])?$configOptions['db_name']:'',$dbport);
287                         if(empty($this->database)) {
288                                 $GLOBALS['log']->fatal("Could not connect to DB server ".$dbhost." as ".$configOptions['db_user_name'].". port " .$dbport . ": " . mysqli_connect_error());
289                                 if($dieOnError) {
290                                         if(isset($GLOBALS['app_strings']['ERR_NO_DB'])) {
291                                                 sugar_die($GLOBALS['app_strings']['ERR_NO_DB']);
292                                         } else {
293                                                 sugar_die("Could not connect to the database. Please refer to sugarcrm.log for details.");
294                                         }
295                                 } else {
296                                         return false;
297                                 }
298                         }
299                 }
300
301                 if(!empty($configOptions['db_name']) && !@mysqli_select_db($this->database,$configOptions['db_name'])) {
302                         $GLOBALS['log']->fatal( "Unable to select database {$configOptions['db_name']}: " . mysqli_connect_error());
303                         if($dieOnError) {
304                                         if(isset($GLOBALS['app_strings']['ERR_NO_DB'])) {
305                                                 sugar_die($GLOBALS['app_strings']['ERR_NO_DB']);
306                                         } else {
307                                                 sugar_die("Could not connect to the database. Please refer to sugarcrm.log for details.");
308                                         }
309                         } else {
310                                 return false;
311                         }
312             }
313
314                 // cn: using direct calls to prevent this from spamming the Logs
315             mysqli_query($this->database,"SET CHARACTER SET utf8");
316             $names = "SET NAMES 'utf8'";
317             $collation = $this->getOption('collation');
318             if(!empty($collation)) {
319                 $names .= " COLLATE '$collation'";
320                 }
321             mysqli_query($this->database,$names);
322
323                 if($this->checkError('Could Not Connect', $dieOnError))
324                         $GLOBALS['log']->info("connected to db");
325
326                 $this->connectOptions = $configOptions;
327                 return true;
328         }
329
330         /**
331          * (non-PHPdoc)
332          * @see MysqlManager::lastDbError()
333          */
334         public function lastDbError()
335         {
336                 if($this->database) {
337                     if(mysqli_errno($this->database)) {
338                             return "MySQL error ".mysqli_errno($this->database).": ".mysqli_error($this->database);
339                     }
340                 } else {
341                         $err =  mysqli_connect_error();
342                         if($err) {
343                             return $err;
344                         }
345                 }
346
347                 return false;
348         }
349
350         public function getDbInfo()
351         {
352                 $charsets = $this->getCharsetInfo();
353                 $charset_str = array();
354                 foreach($charsets as $name => $value) {
355                         $charset_str[] = "$name = $value";
356                 }
357                 return array(
358                         "MySQLi Version" => @mysqli_get_client_info(),
359                         "MySQLi Host Info" => @mysqli_get_host_info($this->database),
360                         "MySQLi Server Info" => @mysqli_get_server_info($this->database),
361                         "MySQLi Client Encoding" =>  @mysqli_client_encoding($this->database),
362                         "MySQL Character Set Settings" => join(", ", $charset_str),
363                 );
364         }
365
366         /**
367          * Select database
368          * @param string $dbname
369          */
370         protected function selectDb($dbname)
371         {
372                 return mysqli_select_db($dbname);
373         }
374
375         /**
376          * Check if this driver can be used
377          * @return bool
378          */
379         public function valid()
380         {
381                 return function_exists("mysqli_connect") && empty($GLOBALS['sugar_config']['mysqli_disabled']);
382         }
383 }