(NFC) CRM-17789 - Replace `@return $this` with types
[civicrm-core.git] / CRM / Core / DAO.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2016 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28 /**
29 * Our base DAO class. All DAO classes should inherit from this class.
30 *
31 * @package CRM
32 * @copyright CiviCRM LLC (c) 2004-2016
33 */
34
35 require_once 'PEAR.php';
36 require_once 'DB/DataObject.php';
37
38 require_once 'CRM/Core/I18n.php';
39
40 /**
41 * Class CRM_Core_DAO
42 */
43 class CRM_Core_DAO extends DB_DataObject {
44
45 /**
46 * A null object so we can pass it as reference if / when needed
47 */
48 static $_nullObject = NULL;
49 static $_nullArray = array();
50
51 static $_dbColumnValueCache = NULL;
52 const NOT_NULL = 1, IS_NULL = 2,
53 DB_DAO_NOTNULL = 128,
54 VALUE_SEPARATOR = "\ 1",
55 BULK_INSERT_COUNT = 200,
56 BULK_INSERT_HIGH_COUNT = 200,
57 QUERY_FORMAT_WILDCARD = 1,
58 QUERY_FORMAT_NO_QUOTES = 2;
59
60 /**
61 * Define entities that shouldn't be created or deleted when creating/ deleting
62 * test objects - this prevents world regions, countries etc from being added / deleted
63 * @var array
64 */
65 static $_testEntitiesToSkip = array();
66 /**
67 * The factory class for this application.
68 * @var object
69 */
70 static $_factory = NULL;
71
72 static $_checkedSqlFunctionsExist = FALSE;
73
74 /**
75 * https://issues.civicrm.org/jira/browse/CRM-17748
76 * internal variable for DAO to hold per-query settings
77 */
78 protected $_options = array();
79
80 /**
81 * Class constructor.
82 *
83 * @return \CRM_Core_DAO
84 */
85 public function __construct() {
86 $this->initialize();
87 $this->__table = $this->getTableName();
88 }
89
90 /**
91 * Empty definition for virtual function.
92 */
93 public static function getTableName() {
94 return NULL;
95 }
96
97 /**
98 * Initialize the DAO object.
99 *
100 * @param string $dsn
101 * The database connection string.
102 */
103 public static function init($dsn) {
104 Civi::$statics[__CLASS__]['init'] = 1;
105 $options = &PEAR::getStaticProperty('DB_DataObject', 'options');
106 $options['database'] = $dsn;
107 if (defined('CIVICRM_DAO_DEBUG')) {
108 self::DebugLevel(CIVICRM_DAO_DEBUG);
109 }
110 $factory = new CRM_Contact_DAO_Factory();
111 CRM_Core_DAO::setFactory($factory);
112 if (CRM_Utils_Constant::value('CIVICRM_MYSQL_STRICT', CRM_Utils_System::isDevelopment())) {
113 CRM_Core_DAO::executeQuery('SET SESSION sql_mode = STRICT_TRANS_TABLES');
114 }
115 CRM_Core_DAO::executeQuery('SET NAMES utf8');
116 CRM_Core_DAO::executeQuery('SET @uniqueID = %1', array(1 => array(CRM_Utils_Request::id(), 'String')));
117 }
118
119 /**
120 * @return DB_common
121 */
122 public static function getConnection() {
123 global $_DB_DATAOBJECT;
124 $dao = new CRM_Core_DAO();
125 return $_DB_DATAOBJECT['CONNECTIONS'][$dao->_database_dsn_md5];
126 }
127
128 /**
129 * @param string $fieldName
130 * @param $fieldDef
131 * @param array $params
132 */
133 protected function assignTestFK($fieldName, $fieldDef, $params) {
134 $required = CRM_Utils_Array::value('required', $fieldDef);
135 $FKClassName = CRM_Utils_Array::value('FKClassName', $fieldDef);
136 $dbName = $fieldDef['name'];
137 $daoName = str_replace('_BAO_', '_DAO_', get_class($this));
138
139 // skip the FK if it is not required
140 // if it's contact id we should create even if not required
141 // we'll have a go @ fetching first though
142 // we WILL create campaigns though for so tests with a campaign pseudoconstant will complete
143 if ($FKClassName === 'CRM_Campaign_DAO_Campaign' && $daoName != $FKClassName) {
144 $required = TRUE;
145 }
146 if (!$required && $dbName != 'contact_id') {
147 $fkDAO = new $FKClassName();
148 if ($fkDAO->find(TRUE)) {
149 $this->$dbName = $fkDAO->id;
150 }
151 $fkDAO->free();
152 }
153
154 elseif (in_array($FKClassName, CRM_Core_DAO::$_testEntitiesToSkip)) {
155 $depObject = new $FKClassName();
156 $depObject->find(TRUE);
157 $this->$dbName = $depObject->id;
158 $depObject->free();
159 }
160 elseif ($daoName == 'CRM_Member_DAO_MembershipType' && $fieldName == 'member_of_contact_id') {
161 // FIXME: the fields() metadata is not specific enough
162 $depObject = CRM_Core_DAO::createTestObject($FKClassName, array('contact_type' => 'Organization'));
163 $this->$dbName = $depObject->id;
164 $depObject->free();
165 }
166 else {
167 //if it is required we need to generate the dependency object first
168 $depObject = CRM_Core_DAO::createTestObject($FKClassName, CRM_Utils_Array::value($dbName, $params, 1));
169 $this->$dbName = $depObject->id;
170 $depObject->free();
171 }
172 }
173
174 /**
175 * Generate and assign an arbitrary value to a field of a test object.
176 *
177 * @param string $fieldName
178 * @param array $fieldDef
179 * @param int $counter
180 * The globally-unique ID of the test object.
181 */
182 protected function assignTestValue($fieldName, &$fieldDef, $counter) {
183 $dbName = $fieldDef['name'];
184 $daoName = get_class($this);
185 $handled = FALSE;
186
187 if (!$handled && $dbName == 'contact_sub_type') {
188 //coming up with a rule to set this is too complex let's not set it
189 $handled = TRUE;
190 }
191
192 // Pick an option value if needed
193 if (!$handled && $fieldDef['type'] !== CRM_Utils_Type::T_BOOLEAN) {
194 $options = $daoName::buildOptions($dbName, 'create');
195 if ($options) {
196 $this->$dbName = key($options);
197 $handled = TRUE;
198 }
199 }
200
201 if (!$handled) {
202 switch ($fieldDef['type']) {
203 case CRM_Utils_Type::T_INT:
204 case CRM_Utils_Type::T_FLOAT:
205 case CRM_Utils_Type::T_MONEY:
206 if (isset($fieldDef['precision'])) {
207 // $object->$dbName = CRM_Utils_Number::createRandomDecimal($value['precision']);
208 $this->$dbName = CRM_Utils_Number::createTruncatedDecimal($counter, $fieldDef['precision']);
209 }
210 else {
211 $this->$dbName = $counter;
212 }
213 break;
214
215 case CRM_Utils_Type::T_BOOLEAN:
216 if (isset($fieldDef['default'])) {
217 $this->$dbName = $fieldDef['default'];
218 }
219 elseif ($fieldDef['name'] == 'is_deleted' || $fieldDef['name'] == 'is_test') {
220 $this->$dbName = 0;
221 }
222 else {
223 $this->$dbName = 1;
224 }
225 break;
226
227 case CRM_Utils_Type::T_DATE:
228 case CRM_Utils_Type::T_TIMESTAMP:
229 case CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME:
230 $this->$dbName = '19700101';
231 if ($dbName == 'end_date') {
232 // put this in the future
233 $this->$dbName = '20200101';
234 }
235 break;
236
237 case CRM_Utils_Type::T_TIME:
238 CRM_Core_Error::fatal("T_TIME shouldn't be used.");
239 //$object->$dbName='000000';
240 //break;
241 case CRM_Utils_Type::T_CCNUM:
242 $this->$dbName = '4111 1111 1111 1111';
243 break;
244
245 case CRM_Utils_Type::T_URL:
246 $this->$dbName = 'http://www.civicrm.org';
247 break;
248
249 case CRM_Utils_Type::T_STRING:
250 case CRM_Utils_Type::T_BLOB:
251 case CRM_Utils_Type::T_MEDIUMBLOB:
252 case CRM_Utils_Type::T_TEXT:
253 case CRM_Utils_Type::T_LONGTEXT:
254 case CRM_Utils_Type::T_EMAIL:
255 default:
256 // WAS: if (isset($value['enumValues'])) {
257 // TODO: see if this works with all pseudoconstants
258 if (isset($fieldDef['pseudoconstant'], $fieldDef['pseudoconstant']['callback'])) {
259 if (isset($fieldDef['default'])) {
260 $this->$dbName = $fieldDef['default'];
261 }
262 else {
263 $options = CRM_Core_PseudoConstant::get($daoName, $fieldName);
264 if (is_array($options)) {
265 $this->$dbName = $options[0];
266 }
267 else {
268 $defaultValues = explode(',', $options);
269 $this->$dbName = $defaultValues[0];
270 }
271 }
272 }
273 else {
274 $this->$dbName = $dbName . '_' . $counter;
275 $maxlength = CRM_Utils_Array::value('maxlength', $fieldDef);
276 if ($maxlength > 0 && strlen($this->$dbName) > $maxlength) {
277 $this->$dbName = substr($this->$dbName, 0, $fieldDef['maxlength']);
278 }
279 }
280 }
281 }
282 }
283
284 /**
285 * Reset the DAO object.
286 *
287 * DAO is kinda crappy in that there is an unwritten rule of one query per DAO.
288 *
289 * We attempt to get around this crappy restriction by resetting some of DAO's internal fields. Use this with caution
290 */
291 public function reset() {
292
293 foreach (array_keys($this->table()) as $field) {
294 unset($this->$field);
295 }
296
297 /**
298 * reset the various DB_DAO structures manually
299 */
300 $this->_query = array();
301 $this->whereAdd();
302 $this->selectAdd();
303 $this->joinAdd();
304 }
305
306 /**
307 * @param string $tableName
308 *
309 * @return string
310 */
311 public static function getLocaleTableName($tableName) {
312 global $dbLocale;
313 if ($dbLocale) {
314 $tables = CRM_Core_I18n_Schema::schemaStructureTables();
315 if (in_array($tableName, $tables)) {
316 return $tableName . $dbLocale;
317 }
318 }
319 return $tableName;
320 }
321
322 /**
323 * Execute a query by the current DAO, localizing it along the way (if needed).
324 *
325 * @param string $query
326 * The SQL query for execution.
327 * @param bool $i18nRewrite
328 * Whether to rewrite the query.
329 *
330 * @return object
331 * the current DAO object after the query execution
332 */
333 public function query($query, $i18nRewrite = TRUE) {
334 // rewrite queries that should use $dbLocale-based views for multi-language installs
335 global $dbLocale, $_DB_DATAOBJECT;
336
337 $conn = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
338 $orig_options = $conn->options;
339 $this->_setDBOptions($this->_options);
340
341 if ($i18nRewrite and $dbLocale) {
342 $query = CRM_Core_I18n_Schema::rewriteQuery($query);
343 }
344
345 $ret = parent::query($query);
346
347 $this->_setDBOptions($orig_options);
348 return $ret;
349 }
350
351 /**
352 * Static function to set the factory instance for this class.
353 *
354 * @param object $factory
355 * The factory application object.
356 */
357 public static function setFactory(&$factory) {
358 self::$_factory = &$factory;
359 }
360
361 /**
362 * Factory method to instantiate a new object from a table name.
363 *
364 * @param string $table
365 */
366 public function factory($table = '') {
367 if (!isset(self::$_factory)) {
368 return parent::factory($table);
369 }
370
371 return self::$_factory->create($table);
372 }
373
374 /**
375 * Initialization for all DAO objects. Since we access DB_DO programatically
376 * we need to set the links manually.
377 */
378 public function initialize() {
379 $this->_connect();
380 if (empty(Civi::$statics[__CLASS__]['init'])) {
381 // CRM_Core_DAO::init() must be called before CRM_Core_DAO->initialize().
382 // This occurs very early in bootstrap - error handlers may not be wired up.
383 echo "Inconsistent system initialization sequence. Premature access of (" . get_class($this) . ")";
384 CRM_Utils_System::civiExit();
385 }
386 }
387
388 /**
389 * Defines the default key as 'id'.
390 *
391 *
392 * @return array
393 */
394 public function keys() {
395 static $keys;
396 if (!isset($keys)) {
397 $keys = array('id');
398 }
399 return $keys;
400 }
401
402 /**
403 * Tells DB_DataObject which keys use autoincrement.
404 * 'id' is autoincrementing by default.
405 *
406 *
407 * @return array
408 */
409 public function sequenceKey() {
410 static $sequenceKeys;
411 if (!isset($sequenceKeys)) {
412 $sequenceKeys = array('id', TRUE);
413 }
414 return $sequenceKeys;
415 }
416
417 /**
418 * Returns list of FK relationships.
419 *
420 *
421 * @return array
422 * Array of CRM_Core_Reference_Interface
423 */
424 public static function getReferenceColumns() {
425 return array();
426 }
427
428 /**
429 * Returns all the column names of this table.
430 *
431 *
432 * @return array
433 */
434 public static function &fields() {
435 $result = NULL;
436 return $result;
437 }
438
439 /**
440 * Get/set an associative array of table columns
441 *
442 * @return array
443 * (associative)
444 */
445 public function table() {
446 $fields = &$this->fields();
447
448 $table = array();
449 if ($fields) {
450 foreach ($fields as $name => $value) {
451 $table[$value['name']] = $value['type'];
452 if (!empty($value['required'])) {
453 $table[$value['name']] += self::DB_DAO_NOTNULL;
454 }
455 }
456 }
457
458 return $table;
459 }
460
461 /**
462 * Save DAO object.
463 *
464 * @param bool $hook
465 *
466 * @return CRM_Core_DAO
467 */
468 public function save($hook = TRUE) {
469 if (!empty($this->id)) {
470 $this->update();
471
472 if ($hook) {
473 $event = new \Civi\Core\DAO\Event\PostUpdate($this);
474 \Civi::service('dispatcher')->dispatch("DAO::post-update", $event);
475 }
476 }
477 else {
478 $this->insert();
479
480 if ($hook) {
481 $event = new \Civi\Core\DAO\Event\PostUpdate($this);
482 \Civi::service('dispatcher')->dispatch("DAO::post-insert", $event);
483 }
484 }
485 $this->free();
486
487 if ($hook) {
488 CRM_Utils_Hook::postSave($this);
489 }
490
491 return $this;
492 }
493
494 /**
495 * Deletes items from table which match current objects variables.
496 *
497 * Returns the true on success
498 *
499 * for example
500 *
501 * Designed to be extended
502 *
503 * $object = new mytable();
504 * $object->ID=123;
505 * echo $object->delete(); // builds a conditon
506 *
507 * $object = new mytable();
508 * $object->whereAdd('age > 12');
509 * $object->limit(1);
510 * $object->orderBy('age DESC');
511 * $object->delete(true); // dont use object vars, use the conditions, limit and order.
512 *
513 * @param bool $useWhere (optional) If DB_DATAOBJECT_WHEREADD_ONLY is passed in then
514 * we will build the condition only using the whereAdd's. Default is to
515 * build the condition only using the object parameters.
516 *
517 * * @return mixed Int (No. of rows affected) on success, false on failure, 0 on no data affected
518 */
519 public function delete($useWhere = FALSE) {
520 $result = parent::delete($useWhere);
521
522 $event = new \Civi\Core\DAO\Event\PostDelete($this, $result);
523 \Civi::service('dispatcher')->dispatch("DAO::post-delete", $event);
524 $this->free();
525
526 return $result;
527 }
528
529 /**
530 * @param bool $created
531 */
532 public function log($created = FALSE) {
533 static $cid = NULL;
534
535 if (!$this->getLog()) {
536 return;
537 }
538
539 if (!$cid) {
540 $session = CRM_Core_Session::singleton();
541 $cid = $session->get('userID');
542 }
543
544 // return is we dont have handle to FK
545 if (!$cid) {
546 return;
547 }
548
549 $dao = new CRM_Core_DAO_Log();
550 $dao->entity_table = $this->getTableName();
551 $dao->entity_id = $this->id;
552 $dao->modified_id = $cid;
553 $dao->modified_date = date("YmdHis");
554 $dao->insert();
555 }
556
557 /**
558 * Given an associative array of name/value pairs, extract all the values
559 * that belong to this object and initialize the object with said values
560 *
561 * @param array $params
562 * (reference ) associative array of name/value pairs.
563 *
564 * @return bool
565 * Did we copy all null values into the object
566 */
567 public function copyValues(&$params) {
568 $fields = &$this->fields();
569 $allNull = TRUE;
570 foreach ($fields as $name => $value) {
571 $dbName = $value['name'];
572 if (array_key_exists($dbName, $params)) {
573 $pValue = $params[$dbName];
574 $exists = TRUE;
575 }
576 elseif (array_key_exists($name, $params)) {
577 $pValue = $params[$name];
578 $exists = TRUE;
579 }
580 else {
581 $exists = FALSE;
582 }
583
584 // if there is no value then make the variable NULL
585 if ($exists) {
586 if ($pValue === '') {
587 $this->$dbName = 'null';
588 }
589 else {
590 $this->$dbName = $pValue;
591 $allNull = FALSE;
592 }
593 }
594 }
595 return $allNull;
596 }
597
598 /**
599 * Store all the values from this object in an associative array
600 * this is a destructive store, calling function is responsible
601 * for keeping sanity of id's.
602 *
603 * @param object $object
604 * The object that we are extracting data from.
605 * @param array $values
606 * (reference ) associative array of name/value pairs.
607 */
608 public static function storeValues(&$object, &$values) {
609 $fields = &$object->fields();
610 foreach ($fields as $name => $value) {
611 $dbName = $value['name'];
612 if (isset($object->$dbName) && $object->$dbName !== 'null') {
613 $values[$dbName] = $object->$dbName;
614 if ($name != $dbName) {
615 $values[$name] = $object->$dbName;
616 }
617 }
618 }
619 }
620
621 /**
622 * Create an attribute for this specific field. We only do this for strings and text
623 *
624 * @param array $field
625 * The field under task.
626 *
627 * @return array|null
628 * the attributes for the object
629 */
630 public static function makeAttribute($field) {
631 if ($field) {
632 if (CRM_Utils_Array::value('type', $field) == CRM_Utils_Type::T_STRING) {
633 $maxLength = CRM_Utils_Array::value('maxlength', $field);
634 $size = CRM_Utils_Array::value('size', $field);
635 if ($maxLength || $size) {
636 $attributes = array();
637 if ($maxLength) {
638 $attributes['maxlength'] = $maxLength;
639 }
640 if ($size) {
641 $attributes['size'] = $size;
642 }
643 return $attributes;
644 }
645 }
646 elseif (CRM_Utils_Array::value('type', $field) == CRM_Utils_Type::T_TEXT) {
647 $rows = CRM_Utils_Array::value('rows', $field);
648 if (!isset($rows)) {
649 $rows = 2;
650 }
651 $cols = CRM_Utils_Array::value('cols', $field);
652 if (!isset($cols)) {
653 $cols = 80;
654 }
655
656 $attributes = array();
657 $attributes['rows'] = $rows;
658 $attributes['cols'] = $cols;
659 return $attributes;
660 }
661 elseif (CRM_Utils_Array::value('type', $field) == CRM_Utils_Type::T_INT || CRM_Utils_Array::value('type', $field) == CRM_Utils_Type::T_FLOAT || CRM_Utils_Array::value('type', $field) == CRM_Utils_Type::T_MONEY) {
662 $attributes['size'] = 6;
663 $attributes['maxlength'] = 14;
664 return $attributes;
665 }
666 }
667 return NULL;
668 }
669
670 /**
671 * Get the size and maxLength attributes for this text field.
672 * (or for all text fields) in the DAO object.
673 *
674 * @param string $class
675 * Name of DAO class.
676 * @param string $fieldName
677 * Field that i'm interested in or null if.
678 * you want the attributes for all DAO text fields
679 *
680 * @return array
681 * assoc array of name => attribute pairs
682 */
683 public static function getAttribute($class, $fieldName = NULL) {
684 $object = new $class();
685 $fields = &$object->fields();
686 if ($fieldName != NULL) {
687 $field = CRM_Utils_Array::value($fieldName, $fields);
688 return self::makeAttribute($field);
689 }
690 else {
691 $attributes = array();
692 foreach ($fields as $name => $field) {
693 $attribute = self::makeAttribute($field);
694 if ($attribute) {
695 $attributes[$name] = $attribute;
696 }
697 }
698
699 if (!empty($attributes)) {
700 return $attributes;
701 }
702 }
703 return NULL;
704 }
705
706 /**
707 * @param $type
708 *
709 * @throws Exception
710 */
711 public static function transaction($type) {
712 CRM_Core_Error::fatal('This function is obsolete, please use CRM_Core_Transaction');
713 }
714
715 /**
716 * Check if there is a record with the same name in the db.
717 *
718 * @param string $value
719 * The value of the field we are checking.
720 * @param string $daoName
721 * The dao object name.
722 * @param string $daoID
723 * The id of the object being updated. u can change your name.
724 * as long as there is no conflict
725 * @param string $fieldName
726 * The name of the field in the DAO.
727 *
728 * @return bool
729 * true if object exists
730 */
731 public static function objectExists($value, $daoName, $daoID, $fieldName = 'name') {
732 $object = new $daoName();
733 $object->$fieldName = $value;
734
735 $config = CRM_Core_Config::singleton();
736
737 if ($object->find(TRUE)) {
738 return ($daoID && $object->id == $daoID) ? TRUE : FALSE;
739 }
740 else {
741 return TRUE;
742 }
743 }
744
745 /**
746 * Check if there is a given column in a specific table.
747 *
748 * @param string $tableName
749 * @param string $columnName
750 * @param bool $i18nRewrite
751 * Whether to rewrite the query on multilingual setups.
752 *
753 * @return bool
754 * true if exists, else false
755 */
756 public static function checkFieldExists($tableName, $columnName, $i18nRewrite = TRUE) {
757 $query = "
758 SHOW COLUMNS
759 FROM $tableName
760 LIKE %1
761 ";
762 $params = array(1 => array($columnName, 'String'));
763 $dao = CRM_Core_DAO::executeQuery($query, $params, TRUE, NULL, FALSE, $i18nRewrite);
764 $result = $dao->fetch() ? TRUE : FALSE;
765 $dao->free();
766 return $result;
767 }
768
769 /**
770 * Scans all the tables using a slow query and table name.
771 *
772 * @return array
773 */
774 public static function getTableNames() {
775 $dao = CRM_Core_DAO::executeQuery(
776 "SELECT TABLE_NAME
777 FROM information_schema.TABLES
778 WHERE TABLE_SCHEMA = '" . CRM_Core_DAO::getDatabaseName() . "'
779 AND TABLE_NAME LIKE 'civicrm_%'
780 AND TABLE_NAME NOT LIKE 'civicrm_import_job_%'
781 AND TABLE_NAME NOT LIKE '%temp'
782 ");
783
784 while ($dao->fetch()) {
785 $values[] = $dao->TABLE_NAME;
786 }
787 $dao->free();
788 return $values;
789 }
790
791 /**
792 * @param int $maxTablesToCheck
793 *
794 * @return bool
795 */
796 public static function isDBMyISAM($maxTablesToCheck = 10) {
797 return CRM_Core_DAO::singleValueQuery(
798 "SELECT count(*)
799 FROM information_schema.TABLES
800 WHERE ENGINE = 'MyISAM'
801 AND TABLE_SCHEMA = '" . CRM_Core_DAO::getDatabaseName() . "'
802 AND TABLE_NAME LIKE 'civicrm_%'
803 AND TABLE_NAME NOT LIKE 'civicrm_import_job_%'
804 AND TABLE_NAME NOT LIKE '%temp'
805 ");
806 }
807
808 /**
809 * Get the name of the CiviCRM database.
810 *
811 * @return string
812 */
813 public static function getDatabaseName() {
814 $daoObj = new CRM_Core_DAO();
815 return $daoObj->database();
816 }
817
818 /**
819 * Checks if a constraint exists for a specified table.
820 *
821 * @param string $tableName
822 * @param string $constraint
823 *
824 * @return bool
825 * true if constraint exists, false otherwise
826 */
827 public static function checkConstraintExists($tableName, $constraint) {
828 static $show = array();
829
830 if (!array_key_exists($tableName, $show)) {
831 $query = "SHOW CREATE TABLE $tableName";
832 $dao = CRM_Core_DAO::executeQuery($query);
833
834 if (!$dao->fetch()) {
835 CRM_Core_Error::fatal();
836 }
837
838 $dao->free();
839 $show[$tableName] = $dao->Create_Table;
840 }
841
842 return preg_match("/\b$constraint\b/i", $show[$tableName]) ? TRUE : FALSE;
843 }
844
845 /**
846 * Checks if CONSTRAINT keyword exists for a specified table.
847 *
848 * @param array $tables
849 *
850 * @throws Exception
851 *
852 * @return bool
853 * true if CONSTRAINT keyword exists, false otherwise
854 */
855 public static function schemaRequiresRebuilding($tables = array("civicrm_contact")) {
856 $show = array();
857 foreach ($tables as $tableName) {
858 if (!array_key_exists($tableName, $show)) {
859 $query = "SHOW CREATE TABLE $tableName";
860 $dao = CRM_Core_DAO::executeQuery($query);
861
862 if (!$dao->fetch()) {
863 CRM_Core_Error::fatal();
864 }
865
866 $dao->free();
867 $show[$tableName] = $dao->Create_Table;
868 }
869
870 $result = preg_match("/\bCONSTRAINT\b\s/i", $show[$tableName]) ? TRUE : FALSE;
871 if ($result == TRUE) {
872 continue;
873 }
874 else {
875 return FALSE;
876 }
877 }
878 return TRUE;
879 }
880
881 /**
882 * Checks if the FK constraint name is in the format 'FK_tableName_columnName'
883 * for a specified column of a table.
884 *
885 * @param string $tableName
886 * @param string $columnName
887 *
888 * @return bool
889 * true if in format, false otherwise
890 */
891 public static function checkFKConstraintInFormat($tableName, $columnName) {
892 static $show = array();
893
894 if (!array_key_exists($tableName, $show)) {
895 $query = "SHOW CREATE TABLE $tableName";
896 $dao = CRM_Core_DAO::executeQuery($query);
897
898 if (!$dao->fetch()) {
899 CRM_Core_Error::fatal();
900 }
901
902 $dao->free();
903 $show[$tableName] = $dao->Create_Table;
904 }
905 $constraint = "`FK_{$tableName}_{$columnName}`";
906 $pattern = "/\bCONSTRAINT\b\s+%s\s+\bFOREIGN\s+KEY\b\s/i";
907 return preg_match(sprintf($pattern, $constraint), $show[$tableName]) ? TRUE : FALSE;
908 }
909
910 /**
911 * Check whether a specific column in a specific table has always the same value.
912 *
913 * @param string $tableName
914 * @param string $columnName
915 * @param string $columnValue
916 *
917 * @return bool
918 * true if the value is always $columnValue, false otherwise
919 */
920 public static function checkFieldHasAlwaysValue($tableName, $columnName, $columnValue) {
921 $query = "SELECT * FROM $tableName WHERE $columnName != '$columnValue'";
922 $dao = CRM_Core_DAO::executeQuery($query);
923 $result = $dao->fetch() ? FALSE : TRUE;
924 $dao->free();
925 return $result;
926 }
927
928 /**
929 * Check whether a specific column in a specific table is always NULL.
930 *
931 * @param string $tableName
932 * @param string $columnName
933 *
934 * @return bool
935 * true if if the value is always NULL, false otherwise
936 */
937 public static function checkFieldIsAlwaysNull($tableName, $columnName) {
938 $query = "SELECT * FROM $tableName WHERE $columnName IS NOT NULL";
939 $dao = CRM_Core_DAO::executeQuery($query);
940 $result = $dao->fetch() ? FALSE : TRUE;
941 $dao->free();
942 return $result;
943 }
944
945 /**
946 * Check if there is a given table in the database.
947 *
948 * @param string $tableName
949 *
950 * @return bool
951 * true if exists, else false
952 */
953 public static function checkTableExists($tableName) {
954 $query = "
955 SHOW TABLES
956 LIKE %1
957 ";
958 $params = array(1 => array($tableName, 'String'));
959
960 $dao = CRM_Core_DAO::executeQuery($query, $params);
961 $result = $dao->fetch() ? TRUE : FALSE;
962 $dao->free();
963 return $result;
964 }
965
966 /**
967 * @param $version
968 *
969 * @return bool
970 */
971 public function checkVersion($version) {
972 $query = "
973 SELECT version
974 FROM civicrm_domain
975 ";
976 $dbVersion = CRM_Core_DAO::singleValueQuery($query);
977 return trim($version) == trim($dbVersion) ? TRUE : FALSE;
978 }
979
980 /**
981 * Find a DAO object for the given ID and return it.
982 *
983 * @param int $id
984 * Id of the DAO object being searched for.
985 *
986 * @return object
987 * Object of the type of the class that called this function.
988 */
989 public static function findById($id) {
990 $object = new static();
991 $object->id = $id;
992 if (!$object->find(TRUE)) {
993 throw new Exception("Unable to find a " . get_called_class() . " with id {$id}.");
994 }
995 return $object;
996 }
997
998 /**
999 * Returns all results as array-encoded records.
1000 *
1001 * @return array
1002 */
1003 public function fetchAll() {
1004 $result = array();
1005 while ($this->fetch()) {
1006 $result[] = $this->toArray();
1007 }
1008 return $result;
1009 }
1010
1011 /**
1012 * Given a DAO name, a column name and a column value, find the record and GET the value of another column in that record
1013 *
1014 * @param string $daoName
1015 * Name of the DAO (Example: CRM_Contact_DAO_Contact to retrieve value from a contact).
1016 * @param int $searchValue
1017 * Value of the column you want to search by.
1018 * @param string $returnColumn
1019 * Name of the column you want to GET the value of.
1020 * @param string $searchColumn
1021 * Name of the column you want to search by.
1022 * @param bool $force
1023 * Skip use of the cache.
1024 *
1025 * @return string|null
1026 * Value of $returnColumn in the retrieved record
1027 */
1028 public static function getFieldValue($daoName, $searchValue, $returnColumn = 'name', $searchColumn = 'id', $force = FALSE) {
1029 if (
1030 empty($searchValue) ||
1031 trim(strtolower($searchValue)) == 'null'
1032 ) {
1033 // adding this year since developers forget to check for an id
1034 // or for the 'null' (which is a bad DAO kludge)
1035 // and hence we get the first value in the db
1036 CRM_Core_Error::fatal();
1037 }
1038
1039 $cacheKey = "{$daoName}:{$searchValue}:{$returnColumn}:{$searchColumn}";
1040 if (self::$_dbColumnValueCache === NULL) {
1041 self::$_dbColumnValueCache = array();
1042 }
1043
1044 if (!array_key_exists($cacheKey, self::$_dbColumnValueCache) || $force) {
1045 $object = new $daoName();
1046 $object->$searchColumn = $searchValue;
1047 $object->selectAdd();
1048 $object->selectAdd($returnColumn);
1049
1050 $result = NULL;
1051 if ($object->find(TRUE)) {
1052 $result = $object->$returnColumn;
1053 }
1054 $object->free();
1055
1056 self::$_dbColumnValueCache[$cacheKey] = $result;
1057 }
1058 return self::$_dbColumnValueCache[$cacheKey];
1059 }
1060
1061 /**
1062 * Given a DAO name, a column name and a column value, find the record and SET the value of another column in that record
1063 *
1064 * @param string $daoName
1065 * Name of the DAO (Example: CRM_Contact_DAO_Contact to retrieve value from a contact).
1066 * @param int $searchValue
1067 * Value of the column you want to search by.
1068 * @param string $setColumn
1069 * Name of the column you want to SET the value of.
1070 * @param string $setValue
1071 * SET the setColumn to this value.
1072 * @param string $searchColumn
1073 * Name of the column you want to search by.
1074 *
1075 * @return bool
1076 * true if we found and updated the object, else false
1077 */
1078 public static function setFieldValue($daoName, $searchValue, $setColumn, $setValue, $searchColumn = 'id') {
1079 $object = new $daoName();
1080 $object->selectAdd();
1081 $object->selectAdd("$searchColumn, $setColumn");
1082 $object->$searchColumn = $searchValue;
1083 $result = FALSE;
1084 if ($object->find(TRUE)) {
1085 $object->$setColumn = $setValue;
1086 if ($object->save()) {
1087 $result = TRUE;
1088 }
1089 }
1090 $object->free();
1091 return $result;
1092 }
1093
1094 /**
1095 * Get sort string.
1096 *
1097 * @param array|object $sort either array or CRM_Utils_Sort
1098 * @param string $default
1099 * Default sort value.
1100 *
1101 * @return string
1102 * sortString
1103 */
1104 public static function getSortString($sort, $default = NULL) {
1105 // check if sort is of type CRM_Utils_Sort
1106 if (is_a($sort, 'CRM_Utils_Sort')) {
1107 return $sort->orderBy();
1108 }
1109
1110 // is it an array specified as $field => $sortDirection ?
1111 if ($sort) {
1112 foreach ($sort as $k => $v) {
1113 $sortString .= "$k $v,";
1114 }
1115 return rtrim($sortString, ',');
1116 }
1117 return $default;
1118 }
1119
1120 /**
1121 * Fetch object based on array of properties.
1122 *
1123 * @param string $daoName
1124 * Name of the dao object.
1125 * @param array $params
1126 * (reference ) an assoc array of name/value pairs.
1127 * @param array $defaults
1128 * (reference ) an assoc array to hold the flattened values.
1129 * @param array $returnProperities
1130 * An assoc array of fields that need to be returned, eg array( 'first_name', 'last_name').
1131 *
1132 * @return object
1133 * an object of type referenced by daoName
1134 */
1135 public static function commonRetrieve($daoName, &$params, &$defaults, $returnProperities = NULL) {
1136 $object = new $daoName();
1137 $object->copyValues($params);
1138
1139 // return only specific fields if returnproperties are sent
1140 if (!empty($returnProperities)) {
1141 $object->selectAdd();
1142 $object->selectAdd(implode(',', $returnProperities));
1143 }
1144
1145 if ($object->find(TRUE)) {
1146 self::storeValues($object, $defaults);
1147 return $object;
1148 }
1149 return NULL;
1150 }
1151
1152 /**
1153 * Delete the object records that are associated with this contact.
1154 *
1155 * @param string $daoName
1156 * Name of the dao object.
1157 * @param int $contactId
1158 * Id of the contact to delete.
1159 */
1160 public static function deleteEntityContact($daoName, $contactId) {
1161 $object = new $daoName();
1162
1163 $object->entity_table = 'civicrm_contact';
1164 $object->entity_id = $contactId;
1165 $object->delete();
1166 }
1167
1168 /**
1169 * execute an unbuffered query. This is a wrapper around new functionality
1170 * exposed with CRM-17748.
1171 *
1172 * @param string $query query to be executed
1173 *
1174 * @return Object CRM_Core_DAO object that points to an unbuffered result set
1175 * @static
1176 * @access public
1177 */
1178 static public function executeUnbufferedQuery(
1179 $query,
1180 $params = array(),
1181 $abort = TRUE,
1182 $daoName = NULL,
1183 $freeDAO = FALSE,
1184 $i18nRewrite = TRUE,
1185 $trapException = FALSE
1186 ) {
1187 $queryStr = self::composeQuery($query, $params, $abort);
1188 //CRM_Core_Error::debug( 'q', $queryStr );
1189 if (!$daoName) {
1190 $dao = new CRM_Core_DAO();
1191 }
1192 else {
1193 $dao = new $daoName();
1194 }
1195
1196 if ($trapException) {
1197 CRM_Core_Error::ignoreException();
1198 }
1199
1200 // set the DAO object to use an unbuffered query
1201 $dao->setOptions(array('result_buffering' => 0));
1202
1203 $result = $dao->query($queryStr, $i18nRewrite);
1204
1205 if ($trapException) {
1206 CRM_Core_Error::setCallback();
1207 }
1208
1209 if (is_a($result, 'DB_Error')) {
1210 return $result;
1211 }
1212
1213 // since it is unbuffered, ($dao->N==0) is true. This blocks the standard fetch() mechanism.
1214 $dao->N = TRUE;
1215
1216 if ($freeDAO ||
1217 preg_match('/^(insert|update|delete|create|drop|replace)/i', $queryStr)
1218 ) {
1219 // we typically do this for insert/update/delete stataments OR if explicitly asked to
1220 // free the dao
1221 $dao->free();
1222 }
1223 return $dao;
1224 }
1225
1226 /**
1227 * Execute a query.
1228 *
1229 * @param string $query
1230 * Query to be executed.
1231 *
1232 * @param array $params
1233 * @param bool $abort
1234 * @param null $daoName
1235 * @param bool $freeDAO
1236 * @param bool $i18nRewrite
1237 * @param bool $trapException
1238 *
1239 * @return CRM_Core_DAO|object
1240 * object that holds the results of the query
1241 * NB - if this is defined as just returning a DAO phpstorm keeps pointing
1242 * out all the properties that are not part of the DAO
1243 */
1244 public static function &executeQuery(
1245 $query,
1246 $params = array(),
1247 $abort = TRUE,
1248 $daoName = NULL,
1249 $freeDAO = FALSE,
1250 $i18nRewrite = TRUE,
1251 $trapException = FALSE
1252 ) {
1253 $queryStr = self::composeQuery($query, $params, $abort);
1254
1255 if (!$daoName) {
1256 $dao = new CRM_Core_DAO();
1257 }
1258 else {
1259 $dao = new $daoName();
1260 }
1261
1262 if ($trapException) {
1263 $errorScope = CRM_Core_TemporaryErrorScope::ignoreException();
1264 }
1265
1266 $result = $dao->query($queryStr, $i18nRewrite);
1267
1268 if (is_a($result, 'DB_Error')) {
1269 return $result;
1270 }
1271
1272 if ($freeDAO ||
1273 preg_match('/^(insert|update|delete|create|drop|replace)/i', $queryStr)
1274 ) {
1275 // we typically do this for insert/update/delete statements OR if explicitly asked to
1276 // free the dao
1277 $dao->free();
1278 }
1279 return $dao;
1280 }
1281
1282 /**
1283 * Execute a query and get the single result.
1284 *
1285 * @param string $query
1286 * Query to be executed.
1287 * @param array $params
1288 * @param bool $abort
1289 * @param bool $i18nRewrite
1290 * @return string|null
1291 * the result of the query if any
1292 *
1293 */
1294 public static function &singleValueQuery(
1295 $query,
1296 $params = array(),
1297 $abort = TRUE,
1298 $i18nRewrite = TRUE
1299 ) {
1300 $queryStr = self::composeQuery($query, $params, $abort);
1301
1302 static $_dao = NULL;
1303
1304 if (!$_dao) {
1305 $_dao = new CRM_Core_DAO();
1306 }
1307
1308 $_dao->query($queryStr, $i18nRewrite);
1309
1310 $result = $_dao->getDatabaseResult();
1311 $ret = NULL;
1312 if ($result) {
1313 $row = $result->fetchRow();
1314 if ($row) {
1315 $ret = $row[0];
1316 }
1317 }
1318 $_dao->free();
1319 return $ret;
1320 }
1321
1322 /**
1323 * Compose the query by merging the parameters into it.
1324 *
1325 * @param string $query
1326 * @param array $params
1327 * @param bool $abort
1328 *
1329 * @return string
1330 * @throws Exception
1331 */
1332 public static function composeQuery($query, $params, $abort = TRUE) {
1333 $tr = array();
1334 foreach ($params as $key => $item) {
1335 if (is_numeric($key)) {
1336 if (CRM_Utils_Type::validate($item[0], $item[1]) !== NULL) {
1337 $item[0] = self::escapeString($item[0]);
1338 if ($item[1] == 'String' ||
1339 $item[1] == 'Memo' ||
1340 $item[1] == 'Link'
1341 ) {
1342 // Support class constants stipulating wildcard characters and/or
1343 // non-quoting of strings. Also support legacy code which may be
1344 // passing in TRUE or 1 for $item[2], which used to indicate the
1345 // use of wildcard characters.
1346 if (!empty($item[2])) {
1347 if ($item[2] & CRM_Core_DAO::QUERY_FORMAT_WILDCARD || $item[2] === TRUE) {
1348 $item[0] = "'%{$item[0]}%'";
1349 }
1350 elseif (!($item[2] & CRM_Core_DAO::QUERY_FORMAT_NO_QUOTES)) {
1351 $item[0] = "'{$item[0]}'";
1352 }
1353 }
1354 else {
1355 $item[0] = "'{$item[0]}'";
1356 }
1357 }
1358
1359 if (($item[1] == 'Date' || $item[1] == 'Timestamp') &&
1360 strlen($item[0]) == 0
1361 ) {
1362 $item[0] = 'null';
1363 }
1364
1365 $tr['%' . $key] = $item[0];
1366 }
1367 elseif ($abort) {
1368 CRM_Core_Error::fatal("{$item[0]} is not of type {$item[1]}");
1369 }
1370 }
1371 }
1372
1373 return strtr($query, $tr);
1374 }
1375
1376 /**
1377 * @param null $ids
1378 */
1379 public static function freeResult($ids = NULL) {
1380 global $_DB_DATAOBJECT;
1381
1382 if (!$ids) {
1383 if (!$_DB_DATAOBJECT ||
1384 !isset($_DB_DATAOBJECT['RESULTS'])
1385 ) {
1386 return;
1387 }
1388 $ids = array_keys($_DB_DATAOBJECT['RESULTS']);
1389 }
1390
1391 foreach ($ids as $id) {
1392 if (isset($_DB_DATAOBJECT['RESULTS'][$id])) {
1393 if (is_resource($_DB_DATAOBJECT['RESULTS'][$id]->result)) {
1394 mysql_free_result($_DB_DATAOBJECT['RESULTS'][$id]->result);
1395 }
1396 unset($_DB_DATAOBJECT['RESULTS'][$id]);
1397 }
1398
1399 if (isset($_DB_DATAOBJECT['RESULTFIELDS'][$id])) {
1400 unset($_DB_DATAOBJECT['RESULTFIELDS'][$id]);
1401 }
1402 }
1403 }
1404
1405 /**
1406 * make a shallow copy of an object.
1407 * and all the fields in the object
1408 *
1409 * @param string $daoName
1410 * Name of the dao.
1411 * @param array $criteria
1412 * Array of all the fields & values.
1413 * on which basis to copy
1414 * @param array $newData
1415 * Array of all the fields & values.
1416 * to be copied besides the other fields
1417 * @param string $fieldsFix
1418 * Array of fields that you want to prefix/suffix/replace.
1419 * @param string $blockCopyOfDependencies
1420 * Fields that you want to block from.
1421 * getting copied
1422 *
1423 *
1424 * @return CRM_Core_DAO
1425 * the newly created copy of the object
1426 */
1427 public static function &copyGeneric($daoName, $criteria, $newData = NULL, $fieldsFix = NULL, $blockCopyOfDependencies = NULL) {
1428 $object = new $daoName();
1429 if (!$newData) {
1430 $object->id = $criteria['id'];
1431 }
1432 else {
1433 foreach ($criteria as $key => $value) {
1434 $object->$key = $value;
1435 }
1436 }
1437
1438 $object->find();
1439 while ($object->fetch()) {
1440
1441 // all the objects except with $blockCopyOfDependencies set
1442 // be copied - addresses #CRM-1962
1443
1444 if ($blockCopyOfDependencies && $object->$blockCopyOfDependencies) {
1445 break;
1446 }
1447
1448 $newObject = new $daoName();
1449
1450 $fields = &$object->fields();
1451 if (!is_array($fieldsFix)) {
1452 $fieldsToPrefix = array();
1453 $fieldsToSuffix = array();
1454 $fieldsToReplace = array();
1455 }
1456 if (!empty($fieldsFix['prefix'])) {
1457 $fieldsToPrefix = $fieldsFix['prefix'];
1458 }
1459 if (!empty($fieldsFix['suffix'])) {
1460 $fieldsToSuffix = $fieldsFix['suffix'];
1461 }
1462 if (!empty($fieldsFix['replace'])) {
1463 $fieldsToReplace = $fieldsFix['replace'];
1464 }
1465
1466 foreach ($fields as $name => $value) {
1467 if ($name == 'id' || $value['name'] == 'id') {
1468 // copy everything but the id!
1469 continue;
1470 }
1471
1472 $dbName = $value['name'];
1473 $type = CRM_Utils_Type::typeToString($value['type']);
1474 $newObject->$dbName = $object->$dbName;
1475 if (isset($fieldsToPrefix[$dbName])) {
1476 $newObject->$dbName = $fieldsToPrefix[$dbName] . $newObject->$dbName;
1477 }
1478 if (isset($fieldsToSuffix[$dbName])) {
1479 $newObject->$dbName .= $fieldsToSuffix[$dbName];
1480 }
1481 if (isset($fieldsToReplace[$dbName])) {
1482 $newObject->$dbName = $fieldsToReplace[$dbName];
1483 }
1484
1485 if ($type == 'Timestamp' || $type == 'Date') {
1486 $newObject->$dbName = CRM_Utils_Date::isoToMysql($newObject->$dbName);
1487 }
1488
1489 if ($newData) {
1490 foreach ($newData as $k => $v) {
1491 $newObject->$k = $v;
1492 }
1493 }
1494 }
1495 $newObject->save();
1496 }
1497 return $newObject;
1498 }
1499
1500 /**
1501 * Cascade update through related entities.
1502 *
1503 * @param string $daoName
1504 * @param $fromId
1505 * @param $toId
1506 * @param array $newData
1507 *
1508 * @return null
1509 */
1510 public static function cascadeUpdate($daoName, $fromId, $toId, $newData = array()) {
1511 $object = new $daoName();
1512 $object->id = $fromId;
1513
1514 if ($object->find(TRUE)) {
1515 $newObject = new $daoName();
1516 $newObject->id = $toId;
1517
1518 if ($newObject->find(TRUE)) {
1519 $fields = &$object->fields();
1520 foreach ($fields as $name => $value) {
1521 if ($name == 'id' || $value['name'] == 'id') {
1522 // copy everything but the id!
1523 continue;
1524 }
1525
1526 $colName = $value['name'];
1527 $newObject->$colName = $object->$colName;
1528
1529 if (substr($name, -5) == '_date' ||
1530 substr($name, -10) == '_date_time'
1531 ) {
1532 $newObject->$colName = CRM_Utils_Date::isoToMysql($newObject->$colName);
1533 }
1534 }
1535 foreach ($newData as $k => $v) {
1536 $newObject->$k = $v;
1537 }
1538 $newObject->save();
1539 return $newObject;
1540 }
1541 }
1542 return CRM_Core_DAO::$_nullObject;
1543 }
1544
1545 /**
1546 * Given the component id, compute the contact id
1547 * since its used for things like send email
1548 *
1549 * @param $componentIDs
1550 * @param string $tableName
1551 *
1552 * @return array
1553 */
1554 public static function &getContactIDsFromComponent(&$componentIDs, $tableName) {
1555 $contactIDs = array();
1556
1557 if (empty($componentIDs)) {
1558 return $contactIDs;
1559 }
1560
1561 $IDs = implode(',', $componentIDs);
1562 $query = "
1563 SELECT contact_id
1564 FROM $tableName
1565 WHERE id IN ( $IDs )
1566 ";
1567
1568 $dao = CRM_Core_DAO::executeQuery($query);
1569 while ($dao->fetch()) {
1570 $contactIDs[] = $dao->contact_id;
1571 }
1572 return $contactIDs;
1573 }
1574
1575 /**
1576 * Fetch object based on array of properties.
1577 *
1578 * @param string $daoName
1579 * Name of the dao object.
1580 * @param string $fieldIdName
1581 * @param int $fieldId
1582 * @param $details
1583 * @param array $returnProperities
1584 * An assoc array of fields that need to be returned, eg array( 'first_name', 'last_name').
1585 *
1586 * @return object
1587 * an object of type referenced by daoName
1588 */
1589 public static function commonRetrieveAll($daoName, $fieldIdName = 'id', $fieldId, &$details, $returnProperities = NULL) {
1590 require_once str_replace('_', DIRECTORY_SEPARATOR, $daoName) . ".php";
1591 $object = new $daoName();
1592 $object->$fieldIdName = $fieldId;
1593
1594 // return only specific fields if returnproperties are sent
1595 if (!empty($returnProperities)) {
1596 $object->selectAdd();
1597 $object->selectAdd('id');
1598 $object->selectAdd(implode(',', $returnProperities));
1599 }
1600
1601 $object->find();
1602 while ($object->fetch()) {
1603 $defaults = array();
1604 self::storeValues($object, $defaults);
1605 $details[$object->id] = $defaults;
1606 }
1607
1608 return $details;
1609 }
1610
1611 public static function dropAllTables() {
1612
1613 // first drop all the custom tables we've created
1614 CRM_Core_BAO_CustomGroup::dropAllTables();
1615
1616 // drop all multilingual views
1617 CRM_Core_I18n_Schema::dropAllViews();
1618
1619 CRM_Utils_File::sourceSQLFile(CIVICRM_DSN,
1620 dirname(__FILE__) . DIRECTORY_SEPARATOR .
1621 '..' . DIRECTORY_SEPARATOR .
1622 '..' . DIRECTORY_SEPARATOR .
1623 'sql' . DIRECTORY_SEPARATOR .
1624 'civicrm_drop.mysql'
1625 );
1626 }
1627
1628 /**
1629 * @param $string
1630 *
1631 * @return string
1632 */
1633 public static function escapeString($string) {
1634 static $_dao = NULL;
1635
1636 if (!$_dao) {
1637 // If this is an atypical case (e.g. preparing .sql files
1638 // before Civi has been installed), then we fallback to
1639 // DB-less escaping helper (mysql_real_escape_string).
1640 // Note: In typical usage, escapeString() will only
1641 // check one conditional ("if !$_dao") rather than
1642 // two conditionals ("if !defined(DSN)")
1643 if (!defined('CIVICRM_DSN')) {
1644 if (function_exists('mysql_real_escape_string')) {
1645 return mysql_real_escape_string($string);
1646 }
1647 else {
1648 throw new CRM_Core_Exception("Cannot generate SQL. \"mysql_real_escape_string\" is missing. Have you installed PHP \"mysql\" extension?");
1649 }
1650 }
1651
1652 $_dao = new CRM_Core_DAO();
1653 }
1654
1655 return $_dao->escape($string);
1656 }
1657
1658 /**
1659 * Escape a list of strings for use with "WHERE X IN (...)" queries.
1660 *
1661 * @param array $strings
1662 * @param string $default
1663 * the value to use if $strings has no elements.
1664 * @return string
1665 * eg "abc","def","ghi"
1666 */
1667 public static function escapeStrings($strings, $default = NULL) {
1668 static $_dao = NULL;
1669 if (!$_dao) {
1670 $_dao = new CRM_Core_DAO();
1671 }
1672
1673 if (empty($strings)) {
1674 return $default;
1675 }
1676
1677 $escapes = array_map(array($_dao, 'escape'), $strings);
1678 return '"' . implode('","', $escapes) . '"';
1679 }
1680
1681 /**
1682 * @param $string
1683 *
1684 * @return string
1685 */
1686 public static function escapeWildCardString($string) {
1687 // CRM-9155
1688 // ensure we escape the single characters % and _ which are mysql wild
1689 // card characters and could come in via sortByCharacter
1690 // note that mysql does not escape these characters
1691 if ($string && in_array($string,
1692 array('%', '_', '%%', '_%')
1693 )
1694 ) {
1695 return '\\' . $string;
1696 }
1697
1698 return self::escapeString($string);
1699 }
1700
1701 /**
1702 * Creates a test object, including any required objects it needs via recursion
1703 * createOnly: only create in database, do not store or return the objects (useful for perf testing)
1704 * ONLY USE FOR TESTING
1705 *
1706 * @param string $daoName
1707 * @param array $params
1708 * @param int $numObjects
1709 * @param bool $createOnly
1710 *
1711 * @return object|array|NULL
1712 * NULL if $createOnly. A single object if $numObjects==1. Otherwise, an array of multiple objects.
1713 */
1714 public static function createTestObject(
1715 $daoName,
1716 $params = array(),
1717 $numObjects = 1,
1718 $createOnly = FALSE
1719 ) {
1720 //this is a test function also backtrace is set for the test suite it sometimes unsets itself
1721 // so we re-set here in case
1722 $config = CRM_Core_Config::singleton();
1723 $config->backtrace = TRUE;
1724
1725 static $counter = 0;
1726 CRM_Core_DAO::$_testEntitiesToSkip = array(
1727 'CRM_Core_DAO_Worldregion',
1728 'CRM_Core_DAO_StateProvince',
1729 'CRM_Core_DAO_Country',
1730 'CRM_Core_DAO_Domain',
1731 'CRM_Financial_DAO_FinancialType',
1732 //because valid ones exist & we use pick them due to pseudoconstant can't reliably create & delete these
1733 );
1734
1735 // Prefer to instantiate BAO's instead of DAO's (when possible)
1736 // so that assignTestValue()/assignTestFK() can be overloaded.
1737 $baoName = str_replace('_DAO_', '_BAO_', $daoName);
1738 if (class_exists($baoName)) {
1739 $daoName = $baoName;
1740 }
1741
1742 for ($i = 0; $i < $numObjects; ++$i) {
1743
1744 ++$counter;
1745 /** @var CRM_Core_DAO $object */
1746 $object = new $daoName();
1747
1748 $fields = &$object->fields();
1749 foreach ($fields as $fieldName => $fieldDef) {
1750 $dbName = $fieldDef['name'];
1751 $FKClassName = CRM_Utils_Array::value('FKClassName', $fieldDef);
1752 $required = CRM_Utils_Array::value('required', $fieldDef);
1753
1754 if (CRM_Utils_Array::value($dbName, $params) !== NULL && !is_array($params[$dbName])) {
1755 $object->$dbName = $params[$dbName];
1756 }
1757
1758 elseif ($dbName != 'id') {
1759 if ($FKClassName != NULL) {
1760 $object->assignTestFK($fieldName, $fieldDef, $params);
1761 continue;
1762 }
1763 else {
1764 $object->assignTestValue($fieldName, $fieldDef, $counter);
1765 }
1766 }
1767 }
1768
1769 $object->save();
1770
1771 if (!$createOnly) {
1772 $objects[$i] = $object;
1773 }
1774 else {
1775 unset($object);
1776 }
1777 }
1778
1779 if ($createOnly) {
1780 return NULL;
1781 }
1782 elseif ($numObjects == 1) {
1783 return $objects[0];
1784 }
1785 else {
1786 return $objects;
1787 }
1788 }
1789
1790 /**
1791 * Deletes the this object plus any dependent objects that are associated with it.
1792 * ONLY USE FOR TESTING
1793 *
1794 * @param string $daoName
1795 * @param array $params
1796 */
1797 public static function deleteTestObjects($daoName, $params = array()) {
1798 //this is a test function also backtrace is set for the test suite it sometimes unsets itself
1799 // so we re-set here in case
1800 $config = CRM_Core_Config::singleton();
1801 $config->backtrace = TRUE;
1802
1803 $object = new $daoName();
1804 $object->id = CRM_Utils_Array::value('id', $params);
1805
1806 $deletions = array(); // array(array(0 => $daoName, 1 => $daoParams))
1807 if ($object->find(TRUE)) {
1808
1809 $fields = &$object->fields();
1810 foreach ($fields as $name => $value) {
1811
1812 $dbName = $value['name'];
1813
1814 $FKClassName = CRM_Utils_Array::value('FKClassName', $value);
1815 $required = CRM_Utils_Array::value('required', $value);
1816 if ($FKClassName != NULL
1817 && $object->$dbName
1818 && !in_array($FKClassName, CRM_Core_DAO::$_testEntitiesToSkip)
1819 && ($required || $dbName == 'contact_id')
1820 //I'm a bit stuck on this one - we might need to change the singleValueAlter so that the entities don't share a contact
1821 // to make this test process pass - line below makes pass for now
1822 && $dbName != 'member_of_contact_id'
1823 ) {
1824 $deletions[] = array($FKClassName, array('id' => $object->$dbName)); // x
1825 }
1826 }
1827 }
1828
1829 $object->delete();
1830
1831 foreach ($deletions as $deletion) {
1832 CRM_Core_DAO::deleteTestObjects($deletion[0], $deletion[1]);
1833 }
1834 }
1835
1836 /**
1837 * Set defaults when creating new entity.
1838 * (don't call this set defaults as already in use with different signature in some places)
1839 *
1840 * @param array $params
1841 * @param $defaults
1842 */
1843 public static function setCreateDefaults(&$params, $defaults) {
1844 if (!empty($params['id'])) {
1845 return;
1846 }
1847 foreach ($defaults as $key => $value) {
1848 if (!array_key_exists($key, $params) || $params[$key] === NULL) {
1849 $params[$key] = $value;
1850 }
1851 }
1852 }
1853
1854 /**
1855 * @param string $prefix
1856 * @param bool $addRandomString
1857 * @param null $string
1858 *
1859 * @return string
1860 */
1861 public static function createTempTableName($prefix = 'civicrm', $addRandomString = TRUE, $string = NULL) {
1862 $tableName = $prefix . "_temp";
1863
1864 if ($addRandomString) {
1865 if ($string) {
1866 $tableName .= "_" . $string;
1867 }
1868 else {
1869 $tableName .= "_" . md5(uniqid('', TRUE));
1870 }
1871 }
1872 return $tableName;
1873 }
1874
1875 /**
1876 * @param bool $view
1877 * @param bool $trigger
1878 *
1879 * @return bool
1880 */
1881 public static function checkTriggerViewPermission($view = TRUE, $trigger = TRUE) {
1882 // test for create view and trigger permissions and if allowed, add the option to go multilingual
1883 // and logging
1884 // I'm not sure why we use the getStaticProperty for an error, rather than checking for DB_Error
1885 $errorScope = CRM_Core_TemporaryErrorScope::ignoreException();
1886 $dao = new CRM_Core_DAO();
1887 if ($view) {
1888 $result = $dao->query('CREATE OR REPLACE VIEW civicrm_domain_view AS SELECT * FROM civicrm_domain');
1889 if (PEAR::getStaticProperty('DB_DataObject', 'lastError') || is_a($result, 'DB_Error')) {
1890 return FALSE;
1891 }
1892 }
1893
1894 if ($trigger) {
1895 $result = $dao->query('CREATE TRIGGER civicrm_domain_trigger BEFORE INSERT ON civicrm_domain FOR EACH ROW BEGIN END');
1896 if (PEAR::getStaticProperty('DB_DataObject', 'lastError') || is_a($result, 'DB_Error')) {
1897 if ($view) {
1898 $dao->query('DROP VIEW IF EXISTS civicrm_domain_view');
1899 }
1900 return FALSE;
1901 }
1902
1903 $dao->query('DROP TRIGGER IF EXISTS civicrm_domain_trigger');
1904 if (PEAR::getStaticProperty('DB_DataObject', 'lastError')) {
1905 if ($view) {
1906 $dao->query('DROP VIEW IF EXISTS civicrm_domain_view');
1907 }
1908 return FALSE;
1909 }
1910 }
1911
1912 if ($view) {
1913 $dao->query('DROP VIEW IF EXISTS civicrm_domain_view');
1914 if (PEAR::getStaticProperty('DB_DataObject', 'lastError')) {
1915 return FALSE;
1916 }
1917 }
1918
1919 return TRUE;
1920 }
1921
1922 /**
1923 * @param null $message
1924 * @param bool $printDAO
1925 */
1926 public static function debugPrint($message = NULL, $printDAO = TRUE) {
1927 CRM_Utils_System::xMemory("{$message}: ");
1928
1929 if ($printDAO) {
1930 global $_DB_DATAOBJECT;
1931 $q = array();
1932 foreach (array_keys($_DB_DATAOBJECT['RESULTS']) as $id) {
1933 $q[] = $_DB_DATAOBJECT['RESULTS'][$id]->query;
1934 }
1935 CRM_Core_Error::debug('_DB_DATAOBJECT', $q);
1936 }
1937 }
1938
1939 /**
1940 * Build a list of triggers via hook and add them to (err, reconcile them
1941 * with) the database.
1942 *
1943 * @param string $tableName
1944 * the specific table requiring a rebuild; or NULL to rebuild all tables.
1945 * @param bool $force
1946 * @deprecated
1947 *
1948 * @see CRM-9716
1949 */
1950 public static function triggerRebuild($tableName = NULL, $force = FALSE) {
1951 Civi::service('sql_triggers')->rebuild($tableName, $force);
1952 }
1953
1954 /**
1955 * Because sql functions are sometimes lost, esp during db migration, we check here to avoid numerous support requests
1956 * @see http://issues.civicrm.org/jira/browse/CRM-13822
1957 * TODO: Alternative solutions might be
1958 * * Stop using functions and find another way to strip numeric characters from phones
1959 * * Give better error messages (currently a missing fn fatals with "unknown error")
1960 */
1961 public static function checkSqlFunctionsExist() {
1962 if (!self::$_checkedSqlFunctionsExist) {
1963 self::$_checkedSqlFunctionsExist = TRUE;
1964 $dao = CRM_Core_DAO::executeQuery("SHOW function status WHERE db = database() AND name = 'civicrm_strip_non_numeric'");
1965 if (!$dao->fetch()) {
1966 self::triggerRebuild();
1967 }
1968 }
1969 }
1970
1971 /**
1972 * Wrapper function to drop triggers.
1973 *
1974 * @param string $tableName
1975 * the specific table requiring a rebuild; or NULL to rebuild all tables.
1976 * @deprecated
1977 */
1978 public static function dropTriggers($tableName = NULL) {
1979 Civi::service('sql_triggers')->dropTriggers($tableName);
1980 }
1981
1982 /**
1983 * @param array $info
1984 * per hook_civicrm_triggerInfo.
1985 * @param string $onlyTableName
1986 * the specific table requiring a rebuild; or NULL to rebuild all tables.
1987 * @deprecated
1988 */
1989 public static function createTriggers(&$info, $onlyTableName = NULL) {
1990 Civi::service('sql_triggers')->createTriggers($info, $onlyTableName);
1991 }
1992
1993 /**
1994 * Given a list of fields, create a list of references.
1995 *
1996 * @param string $className
1997 * BAO/DAO class name.
1998 * @return array<CRM_Core_Reference_Interface>
1999 */
2000 public static function createReferenceColumns($className) {
2001 $result = array();
2002 $fields = $className::fields();
2003 foreach ($fields as $field) {
2004 if (isset($field['pseudoconstant'], $field['pseudoconstant']['optionGroupName'])) {
2005 $result[] = new CRM_Core_Reference_OptionValue(
2006 $className::getTableName(),
2007 $field['name'],
2008 'civicrm_option_value',
2009 CRM_Utils_Array::value('keyColumn', $field['pseudoconstant'], 'value'),
2010 $field['pseudoconstant']['optionGroupName']
2011 );
2012 }
2013 }
2014 return $result;
2015 }
2016
2017 /**
2018 * Find all records which refer to this entity.
2019 *
2020 * @return array
2021 * Array of objects referencing this
2022 */
2023 public function findReferences() {
2024 $links = self::getReferencesToTable(static::getTableName());
2025
2026 $occurrences = array();
2027 foreach ($links as $refSpec) {
2028 /** @var $refSpec CRM_Core_Reference_Interface */
2029 $daoName = CRM_Core_DAO_AllCoreTables::getClassForTable($refSpec->getReferenceTable());
2030 $result = $refSpec->findReferences($this);
2031 if ($result) {
2032 while ($result->fetch()) {
2033 $obj = new $daoName();
2034 $obj->id = $result->id;
2035 $occurrences[] = $obj;
2036 }
2037 }
2038 }
2039
2040 return $occurrences;
2041 }
2042
2043 /**
2044 * @return array
2045 * each item has keys:
2046 * - name: string
2047 * - type: string
2048 * - count: int
2049 * - table: string|null SQL table name
2050 * - key: string|null SQL column name
2051 */
2052 public function getReferenceCounts() {
2053 $links = self::getReferencesToTable(static::getTableName());
2054
2055 $counts = array();
2056 foreach ($links as $refSpec) {
2057 /** @var $refSpec CRM_Core_Reference_Interface */
2058 $count = $refSpec->getReferenceCount($this);
2059 if ($count['count'] != 0) {
2060 $counts[] = $count;
2061 }
2062 }
2063
2064 foreach (CRM_Core_Component::getEnabledComponents() as $component) {
2065 /** @var $component CRM_Core_Component_Info */
2066 $counts = array_merge($counts, $component->getReferenceCounts($this));
2067 }
2068 CRM_Utils_Hook::referenceCounts($this, $counts);
2069
2070 return $counts;
2071 }
2072
2073 /**
2074 * List all tables which have hard foreign keys to this table.
2075 *
2076 * For now, this returns a description of every entity_id/entity_table
2077 * reference.
2078 * TODO: filter dynamic entity references on the $tableName, based on
2079 * schema metadata in dynamicForeignKey which enumerates a restricted
2080 * set of possible entity_table's.
2081 *
2082 * @param string $tableName
2083 * Table referred to.
2084 *
2085 * @return array
2086 * structure of table and column, listing every table with a
2087 * foreign key reference to $tableName, and the column where the key appears.
2088 */
2089 public static function getReferencesToTable($tableName) {
2090 $refsFound = array();
2091 foreach (CRM_Core_DAO_AllCoreTables::getClasses() as $daoClassName) {
2092 $links = $daoClassName::getReferenceColumns();
2093 $daoTableName = $daoClassName::getTableName();
2094
2095 foreach ($links as $refSpec) {
2096 /** @var $refSpec CRM_Core_Reference_Interface */
2097 if ($refSpec->matchesTargetTable($tableName)) {
2098 $refsFound[] = $refSpec;
2099 }
2100 }
2101 }
2102 return $refsFound;
2103 }
2104
2105 /**
2106 * Lookup the value of a MySQL global configuration variable.
2107 *
2108 * @param string $name
2109 * E.g. "thread_stack".
2110 * @param mixed $default
2111 * @return mixed
2112 */
2113 public static function getGlobalSetting($name, $default = NULL) {
2114 // Alternatively, SELECT @@GLOBAL.thread_stack, but
2115 // that has been reported to fail under MySQL 5.0 for OS X
2116 $escapedName = self::escapeString($name);
2117 $dao = CRM_Core_DAO::executeQuery("SHOW VARIABLES LIKE '$escapedName'");
2118 if ($dao->fetch()) {
2119 return $dao->Value;
2120 }
2121 else {
2122 return $default;
2123 }
2124 }
2125
2126 /**
2127 * Get options for the called BAO object's field.
2128 *
2129 * This function can be overridden by each BAO to add more logic related to context.
2130 * The overriding function will generally call the lower-level CRM_Core_PseudoConstant::get
2131 *
2132 * @param string $fieldName
2133 * @param string $context
2134 * @see CRM_Core_DAO::buildOptionsContext
2135 * @param array $props
2136 * whatever is known about this bao object.
2137 *
2138 * @return array|bool
2139 */
2140 public static function buildOptions($fieldName, $context = NULL, $props = array()) {
2141 // If a given bao does not override this function
2142 $baoName = get_called_class();
2143 return CRM_Core_PseudoConstant::get($baoName, $fieldName, array(), $context);
2144 }
2145
2146 /**
2147 * Populate option labels for this object's fields.
2148 *
2149 * @throws exception if called directly on the base class
2150 */
2151 public function getOptionLabels() {
2152 $fields = $this->fields();
2153 if ($fields === NULL) {
2154 throw new Exception('Cannot call getOptionLabels on CRM_Core_DAO');
2155 }
2156 foreach ($fields as $field) {
2157 $name = CRM_Utils_Array::value('name', $field);
2158 if ($name && isset($this->$name)) {
2159 $label = CRM_Core_PseudoConstant::getLabel(get_class($this), $name, $this->$name);
2160 if ($label !== FALSE) {
2161 // Append 'label' onto the field name
2162 $labelName = $name . '_label';
2163 $this->$labelName = $label;
2164 }
2165 }
2166 }
2167 }
2168
2169 /**
2170 * Provides documentation and validation for the buildOptions $context param
2171 *
2172 * @param string $context
2173 *
2174 * @throws Exception
2175 * @return array
2176 */
2177 public static function buildOptionsContext($context = NULL) {
2178 $contexts = array(
2179 'get' => "get: all options are returned, even if they are disabled; labels are translated.",
2180 'create' => "create: options are filtered appropriately for the object being created/updated; labels are translated.",
2181 'search' => "search: searchable options are returned; labels are translated.",
2182 'validate' => "validate: all options are returned, even if they are disabled; machine names are used in place of labels.",
2183 'abbreviate' => "abbreviate: enabled options are returned; labels are replaced with abbreviations.",
2184 'match' => "match: enabled options are returned using machine names as keys; labels are translated.",
2185 );
2186 // Validation: enforce uniformity of this param
2187 if ($context !== NULL && !isset($contexts[$context])) {
2188 throw new Exception("'$context' is not a valid context for buildOptions.");
2189 }
2190 return $contexts;
2191 }
2192
2193 /**
2194 * @param string $fieldName
2195 * @return bool|array
2196 */
2197 public function getFieldSpec($fieldName) {
2198 $fields = $this->fields();
2199 $fieldKeys = $this->fieldKeys();
2200
2201 // Support "unique names" as well as sql names
2202 $fieldKey = $fieldName;
2203 if (empty($fields[$fieldKey])) {
2204 $fieldKey = CRM_Utils_Array::value($fieldName, $fieldKeys);
2205 }
2206 // If neither worked then this field doesn't exist. Return false.
2207 if (empty($fields[$fieldKey])) {
2208 return FALSE;
2209 }
2210 return $fields[$fieldKey];
2211 }
2212
2213 /**
2214 * Get SQL where clause for SQL filter syntax input parameters.
2215 *
2216 * SQL version of api function to assign filters to the DAO based on the syntax
2217 * $field => array('IN' => array(4,6,9))
2218 * OR
2219 * $field => array('LIKE' => array('%me%))
2220 * etc
2221 *
2222 * @param string $fieldName
2223 * Name of fields.
2224 * @param array $filter
2225 * filter to be applied indexed by operator.
2226 * @param string $type
2227 * type of field (not actually used - nor in api @todo ).
2228 * @param string $alias
2229 * alternative field name ('as') @todo- not actually used.
2230 * @param bool $returnSanitisedArray
2231 * Return a sanitised array instead of a clause.
2232 * this is primarily so we can add filters @ the api level to the Query object based fields
2233 *
2234 * @throws Exception
2235 *
2236 * @return NULL|string|array
2237 * a string is returned if $returnSanitisedArray is not set, otherwise and Array or NULL
2238 * depending on whether it is supported as yet
2239 */
2240 public static function createSQLFilter($fieldName, $filter, $type = NULL, $alias = NULL, $returnSanitisedArray = FALSE) {
2241 foreach ($filter as $operator => $criteria) {
2242 if (in_array($operator, self::acceptedSQLOperators(), TRUE)) {
2243 switch ($operator) {
2244 // unary operators
2245 case 'IS NULL':
2246 case 'IS NOT NULL':
2247 if (!$returnSanitisedArray) {
2248 return (sprintf('%s %s', $fieldName, $operator));
2249 }
2250 else {
2251 return (sprintf('%s %s ', $fieldName, $operator));
2252 }
2253 break;
2254
2255 // ternary operators
2256 case 'BETWEEN':
2257 case 'NOT BETWEEN':
2258 if (empty($criteria[0]) || empty($criteria[1])) {
2259 throw new Exception("invalid criteria for $operator");
2260 }
2261 if (!$returnSanitisedArray) {
2262 return (sprintf('%s ' . $operator . ' "%s" AND "%s"', $fieldName, CRM_Core_DAO::escapeString($criteria[0]), CRM_Core_DAO::escapeString($criteria[1])));
2263 }
2264 else {
2265 return NULL; // not yet implemented (tests required to implement)
2266 }
2267 break;
2268
2269 // n-ary operators
2270 case 'IN':
2271 case 'NOT IN':
2272 if (empty($criteria)) {
2273 throw new Exception("invalid criteria for $operator");
2274 }
2275 $escapedCriteria = array_map(array(
2276 'CRM_Core_DAO',
2277 'escapeString',
2278 ), $criteria);
2279 if (!$returnSanitisedArray) {
2280 return (sprintf('%s %s ("%s")', $fieldName, $operator, implode('", "', $escapedCriteria)));
2281 }
2282 return $escapedCriteria;
2283
2284 // binary operators
2285
2286 default:
2287 if (!$returnSanitisedArray) {
2288 return (sprintf('%s %s "%s"', $fieldName, $operator, CRM_Core_DAO::escapeString($criteria)));
2289 }
2290 else {
2291 return NULL; // not yet implemented (tests required to implement)
2292 }
2293 }
2294 }
2295 }
2296 }
2297
2298 /**
2299 * @see http://issues.civicrm.org/jira/browse/CRM-9150
2300 * support for other syntaxes is discussed in ticket but being put off for now
2301 * @return array
2302 */
2303 public static function acceptedSQLOperators() {
2304 return array(
2305 '=',
2306 '<=',
2307 '>=',
2308 '>',
2309 '<',
2310 'LIKE',
2311 "<>",
2312 "!=",
2313 "NOT LIKE",
2314 'IN',
2315 'NOT IN',
2316 'BETWEEN',
2317 'NOT BETWEEN',
2318 'IS NOT NULL',
2319 'IS NULL',
2320 );
2321 }
2322
2323 /**
2324 * SQL has a limit of 64 characters on various names:
2325 * table name, trigger name, column name ...
2326 *
2327 * For custom groups and fields we generated names from user entered input
2328 * which can be longer than this length, this function helps with creating
2329 * strings that meet various criteria.
2330 *
2331 * @param string $string
2332 * The string to be shortened.
2333 * @param int $length
2334 * The max length of the string.
2335 *
2336 * @param bool $makeRandom
2337 *
2338 * @return string
2339 */
2340 public static function shortenSQLName($string, $length = 60, $makeRandom = FALSE) {
2341 // early return for strings that meet the requirements
2342 if (strlen($string) <= $length) {
2343 return $string;
2344 }
2345
2346 // easy return for calls that dont need a randomized uniq string
2347 if (!$makeRandom) {
2348 return substr($string, 0, $length);
2349 }
2350
2351 // the string is longer than the length and we need a uniq string
2352 // for the same tablename we need the same uniq string every time
2353 // hence we use md5 on the string, which is not random
2354 // we'll append 8 characters to the end of the tableName
2355 $md5string = substr(md5($string), 0, 8);
2356 return substr($string, 0, $length - 8) . "_{$md5string}";
2357 }
2358
2359 /**
2360 * https://issues.civicrm.org/jira/browse/CRM-17748
2361 * Sets the internal options to be used on a query
2362 *
2363 * @param array $options
2364 *
2365 */
2366 public function setOptions($options) {
2367 if (is_array($options)) {
2368 $this->_options = $options;
2369 }
2370 }
2371
2372 /**
2373 * https://issues.civicrm.org/jira/browse/CRM-17748
2374 * wrapper to pass internal DAO options down to DB_mysql/DB_Common level
2375 *
2376 * @param array $options
2377 *
2378 */
2379 protected function _setDBOptions($options) {
2380 global $_DB_DATAOBJECT;
2381
2382 if (is_array($options) && count($options)) {
2383 $conn = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
2384 foreach ($options as $option_name => $option_value) {
2385 $conn->setOption($option_name, $option_value);
2386 }
2387 }
2388 }
2389
2390 /**
2391 * @deprecated
2392 * @param array $params
2393 */
2394 public function setApiFilter(&$params) {
2395 }
2396
2397 /**
2398 * Generates acl clauses suitable for adding to WHERE or ON when doing an api.get for this entity
2399 *
2400 * Return format is in the form of fieldname => clauses starting with an operator. e.g.:
2401 * @code
2402 * array(
2403 * 'location_type_id' => array('IS NOT NULL', 'IN (1,2,3)')
2404 * )
2405 * @endcode
2406 *
2407 * Note that all array keys must be actual field names in this entity. Use subqueries to filter on other tables e.g. custom values.
2408 *
2409 * @return array
2410 */
2411 public function addSelectWhereClause() {
2412 // This is the default fallback, and works for contact-related entities like Email, Relationship, etc.
2413 $clauses = array();
2414 foreach ($this->fields() as $fieldName => $field) {
2415 if (strpos($fieldName, 'contact_id') === 0 && CRM_Utils_Array::value('FKClassName', $field) == 'CRM_Contact_DAO_Contact') {
2416 $clauses[$fieldName] = CRM_Utils_SQL::mergeSubquery('Contact');
2417 }
2418 }
2419 CRM_Utils_Hook::selectWhereClause($this, $clauses);
2420 return $clauses;
2421 }
2422
2423 /**
2424 * This returns the final permissioned query string for this entity
2425 *
2426 * With acls from related entities + additional clauses from hook_civicrm_selectWhereClause
2427 *
2428 * @param string $tableAlias
2429 * @return array
2430 */
2431 public static function getSelectWhereClause($tableAlias = NULL) {
2432 $bao = new static();
2433 if ($tableAlias === NULL) {
2434 $tableAlias = $bao->tableName();
2435 }
2436 $clauses = array();
2437 foreach ((array) $bao->addSelectWhereClause() as $field => $vals) {
2438 $clauses[$field] = NULL;
2439 if ($vals) {
2440 $clauses[$field] = "`$tableAlias`.`$field` " . implode(" AND `$tableAlias`.`$field` ", (array) $vals);
2441 }
2442 }
2443 return $clauses;
2444 }
2445
2446 /**
2447 * function to check valid db name containing only characters in [0-9,a-z,A-Z_]
2448 *
2449 * @param $database
2450 *
2451 * @return bool
2452 */
2453 public static function requireValidDBName($database) {
2454 $matches = array();
2455 preg_match(
2456 "/^[0-9]*[a-zA-Z_]+[a-zA-Z0-9_]*$/",
2457 $database,
2458 $matches
2459 );
2460 if (empty($matches)) {
2461 return FALSE;
2462 }
2463 return TRUE;
2464 }
2465
2466 }