[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/lib/plugins/authpdo/ -> auth.php (source)

   1  <?php
   2  /**
   3   * DokuWiki Plugin authpdo (Auth Component)
   4   *
   5   * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
   6   * @author  Andreas Gohr <andi@splitbrain.org>
   7   */
   8  
   9  /**
  10   * Class auth_plugin_authpdo
  11   */
  12  class auth_plugin_authpdo extends DokuWiki_Auth_Plugin
  13  {
  14  
  15      /** @var PDO */
  16      protected $pdo;
  17  
  18      /** @var null|array The list of all groups */
  19      protected $groupcache = null;
  20  
  21      /**
  22       * Constructor.
  23       */
  24      public function __construct()
  25      {
  26          parent::__construct(); // for compatibility
  27  
  28          if (!class_exists('PDO')) {
  29              $this->debugMsg('PDO extension for PHP not found.', -1, __LINE__);
  30              $this->success = false;
  31              return;
  32          }
  33  
  34          if (!$this->getConf('dsn')) {
  35              $this->debugMsg('No DSN specified', -1, __LINE__);
  36              $this->success = false;
  37              return;
  38          }
  39  
  40          try {
  41              $this->pdo = new PDO(
  42                  $this->getConf('dsn'),
  43                  $this->getConf('user'),
  44                  conf_decodeString($this->getConf('pass')),
  45                  array(
  46                      PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // always fetch as array
  47                      PDO::ATTR_EMULATE_PREPARES => true, // emulating prepares allows us to reuse param names
  48                      PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // we want exceptions, not error codes
  49                  )
  50              );
  51          } catch (PDOException $e) {
  52              $this->debugMsg($e);
  53              msg($this->getLang('connectfail'), -1);
  54              $this->success = false;
  55              return;
  56          }
  57  
  58          // can Users be created?
  59          $this->cando['addUser'] = $this->checkConfig(
  60              array(
  61                  'select-user',
  62                  'select-user-groups',
  63                  'select-groups',
  64                  'insert-user',
  65                  'insert-group',
  66                  'join-group'
  67              )
  68          );
  69  
  70          // can Users be deleted?
  71          $this->cando['delUser'] = $this->checkConfig(
  72              array(
  73                  'select-user',
  74                  'select-user-groups',
  75                  'select-groups',
  76                  'leave-group',
  77                  'delete-user'
  78              )
  79          );
  80  
  81          // can login names be changed?
  82          $this->cando['modLogin'] = $this->checkConfig(
  83              array(
  84                  'select-user',
  85                  'select-user-groups',
  86                  'update-user-login'
  87              )
  88          );
  89  
  90          // can passwords be changed?
  91          $this->cando['modPass'] = $this->checkConfig(
  92              array(
  93                  'select-user',
  94                  'select-user-groups',
  95                  'update-user-pass'
  96              )
  97          );
  98  
  99          // can real names be changed?
 100          $this->cando['modName'] = $this->checkConfig(
 101              array(
 102                  'select-user',
 103                  'select-user-groups',
 104                  'update-user-info:name'
 105              )
 106          );
 107  
 108          // can real email be changed?
 109          $this->cando['modMail'] = $this->checkConfig(
 110              array(
 111                  'select-user',
 112                  'select-user-groups',
 113                  'update-user-info:mail'
 114              )
 115          );
 116  
 117          // can groups be changed?
 118          $this->cando['modGroups'] = $this->checkConfig(
 119              array(
 120                  'select-user',
 121                  'select-user-groups',
 122                  'select-groups',
 123                  'leave-group',
 124                  'join-group',
 125                  'insert-group'
 126              )
 127          );
 128  
 129          // can a filtered list of users be retrieved?
 130          $this->cando['getUsers'] = $this->checkConfig(
 131              array(
 132                  'list-users'
 133              )
 134          );
 135  
 136          // can the number of users be retrieved?
 137          $this->cando['getUserCount'] = $this->checkConfig(
 138              array(
 139                  'count-users'
 140              )
 141          );
 142  
 143          // can a list of available groups be retrieved?
 144          $this->cando['getGroups'] = $this->checkConfig(
 145              array(
 146                  'select-groups'
 147              )
 148          );
 149  
 150          $this->success = true;
 151      }
 152  
 153      /**
 154       * Check user+password
 155       *
 156       * @param string $user the user name
 157       * @param string $pass the clear text password
 158       * @return  bool
 159       */
 160      public function checkPass($user, $pass)
 161      {
 162  
 163          $userdata = $this->selectUser($user);
 164          if ($userdata == false) return false;
 165  
 166          // password checking done in SQL?
 167          if ($this->checkConfig(array('check-pass'))) {
 168              $userdata['clear'] = $pass;
 169              $userdata['hash'] = auth_cryptPassword($pass);
 170              $result = $this->query($this->getConf('check-pass'), $userdata);
 171              if ($result === false) return false;
 172              return (count($result) == 1);
 173          }
 174  
 175          // we do password checking on our own
 176          if (isset($userdata['hash'])) {
 177              // hashed password
 178              $passhash = new \dokuwiki\PassHash();
 179              return $passhash->verify_hash($pass, $userdata['hash']);
 180          } else {
 181              // clear text password in the database O_o
 182              return ($pass === $userdata['clear']);
 183          }
 184      }
 185  
 186      /**
 187       * Return user info
 188       *
 189       * Returns info about the given user needs to contain
 190       * at least these fields:
 191       *
 192       * name string  full name of the user
 193       * mail string  email addres of the user
 194       * grps array   list of groups the user is in
 195       *
 196       * @param string $user the user name
 197       * @param bool $requireGroups whether or not the returned data must include groups
 198       * @return array|bool containing user data or false
 199       */
 200      public function getUserData($user, $requireGroups = true)
 201      {
 202          $data = $this->selectUser($user);
 203          if ($data == false) return false;
 204  
 205          if (isset($data['hash'])) unset($data['hash']);
 206          if (isset($data['clean'])) unset($data['clean']);
 207  
 208          if ($requireGroups) {
 209              $data['grps'] = $this->selectUserGroups($data);
 210              if ($data['grps'] === false) return false;
 211          }
 212  
 213          return $data;
 214      }
 215  
 216      /**
 217       * Create a new User [implement only where required/possible]
 218       *
 219       * Returns false if the user already exists, null when an error
 220       * occurred and true if everything went well.
 221       *
 222       * The new user HAS TO be added to the default group by this
 223       * function!
 224       *
 225       * Set addUser capability when implemented
 226       *
 227       * @param string $user
 228       * @param string $clear
 229       * @param string $name
 230       * @param string $mail
 231       * @param null|array $grps
 232       * @return bool|null
 233       */
 234      public function createUser($user, $clear, $name, $mail, $grps = null)
 235      {
 236          global $conf;
 237  
 238          if (($info = $this->getUserData($user, false)) !== false) {
 239              msg($this->getLang('userexists'), -1);
 240              return false; // user already exists
 241          }
 242  
 243          // prepare data
 244          if ($grps == null) $grps = array();
 245          array_unshift($grps, $conf['defaultgroup']);
 246          $grps = array_unique($grps);
 247          $hash = auth_cryptPassword($clear);
 248          $userdata = compact('user', 'clear', 'hash', 'name', 'mail');
 249  
 250          // action protected by transaction
 251          $this->pdo->beginTransaction();
 252          {
 253              // insert the user
 254              $ok = $this->query($this->getConf('insert-user'), $userdata);
 255              if ($ok === false) goto FAIL;
 256              $userdata = $this->getUserData($user, false);
 257              if ($userdata === false) goto FAIL;
 258  
 259              // create all groups that do not exist, the refetch the groups
 260              $allgroups = $this->selectGroups();
 261              foreach ($grps as $group) {
 262                  if (!isset($allgroups[$group])) {
 263                      $ok = $this->addGroup($group);
 264                      if ($ok === false) goto FAIL;
 265                  }
 266              }
 267              $allgroups = $this->selectGroups();
 268  
 269              // add user to the groups
 270              foreach ($grps as $group) {
 271                  $ok = $this->joinGroup($userdata, $allgroups[$group]);
 272                  if ($ok === false) goto FAIL;
 273              }
 274          }
 275          $this->pdo->commit();
 276          return true;
 277  
 278          // something went wrong, rollback
 279          FAIL:
 280          $this->pdo->rollBack();
 281          $this->debugMsg('Transaction rolled back', 0, __LINE__);
 282          msg($this->getLang('writefail'), -1);
 283          return null; // return error
 284      }
 285  
 286      /**
 287       * Modify user data
 288       *
 289       * @param string $user nick of the user to be changed
 290       * @param array $changes array of field/value pairs to be changed (password will be clear text)
 291       * @return  bool
 292       */
 293      public function modifyUser($user, $changes)
 294      {
 295          // secure everything in transaction
 296          $this->pdo->beginTransaction();
 297          {
 298              $olddata = $this->getUserData($user);
 299              $oldgroups = $olddata['grps'];
 300              unset($olddata['grps']);
 301  
 302              // changing the user name?
 303              if (isset($changes['user'])) {
 304                  if ($this->getUserData($changes['user'], false)) goto FAIL;
 305                  $params = $olddata;
 306                  $params['newlogin'] = $changes['user'];
 307  
 308                  $ok = $this->query($this->getConf('update-user-login'), $params);
 309                  if ($ok === false) goto FAIL;
 310              }
 311  
 312              // changing the password?
 313              if (isset($changes['pass'])) {
 314                  $params = $olddata;
 315                  $params['clear'] = $changes['pass'];
 316                  $params['hash'] = auth_cryptPassword($changes['pass']);
 317  
 318                  $ok = $this->query($this->getConf('update-user-pass'), $params);
 319                  if ($ok === false) goto FAIL;
 320              }
 321  
 322              // changing info?
 323              if (isset($changes['mail']) || isset($changes['name'])) {
 324                  $params = $olddata;
 325                  if (isset($changes['mail'])) $params['mail'] = $changes['mail'];
 326                  if (isset($changes['name'])) $params['name'] = $changes['name'];
 327  
 328                  $ok = $this->query($this->getConf('update-user-info'), $params);
 329                  if ($ok === false) goto FAIL;
 330              }
 331  
 332              // changing groups?
 333              if (isset($changes['grps'])) {
 334                  $allgroups = $this->selectGroups();
 335  
 336                  // remove membership for previous groups
 337                  foreach ($oldgroups as $group) {
 338                      if (!in_array($group, $changes['grps']) && isset($allgroups[$group])) {
 339                          $ok = $this->leaveGroup($olddata, $allgroups[$group]);
 340                          if ($ok === false) goto FAIL;
 341                      }
 342                  }
 343  
 344                  // create all new groups that are missing
 345                  $added = 0;
 346                  foreach ($changes['grps'] as $group) {
 347                      if (!isset($allgroups[$group])) {
 348                          $ok = $this->addGroup($group);
 349                          if ($ok === false) goto FAIL;
 350                          $added++;
 351                      }
 352                  }
 353                  // reload group info
 354                  if ($added > 0) $allgroups = $this->selectGroups();
 355  
 356                  // add membership for new groups
 357                  foreach ($changes['grps'] as $group) {
 358                      if (!in_array($group, $oldgroups)) {
 359                          $ok = $this->joinGroup($olddata, $allgroups[$group]);
 360                          if ($ok === false) goto FAIL;
 361                      }
 362                  }
 363              }
 364  
 365          }
 366          $this->pdo->commit();
 367          return true;
 368  
 369          // something went wrong, rollback
 370          FAIL:
 371          $this->pdo->rollBack();
 372          $this->debugMsg('Transaction rolled back', 0, __LINE__);
 373          msg($this->getLang('writefail'), -1);
 374          return false; // return error
 375      }
 376  
 377      /**
 378       * Delete one or more users
 379       *
 380       * Set delUser capability when implemented
 381       *
 382       * @param array $users
 383       * @return  int    number of users deleted
 384       */
 385      public function deleteUsers($users)
 386      {
 387          $count = 0;
 388          foreach ($users as $user) {
 389              if ($this->deleteUser($user)) $count++;
 390          }
 391          return $count;
 392      }
 393  
 394      /**
 395       * Bulk retrieval of user data [implement only where required/possible]
 396       *
 397       * Set getUsers capability when implemented
 398       *
 399       * @param int $start index of first user to be returned
 400       * @param int $limit max number of users to be returned
 401       * @param array $filter array of field/pattern pairs, null for no filter
 402       * @return  array list of userinfo (refer getUserData for internal userinfo details)
 403       */
 404      public function retrieveUsers($start = 0, $limit = -1, $filter = null)
 405      {
 406          if ($limit < 0) $limit = 10000; // we don't support no limit
 407          if (is_null($filter)) $filter = array();
 408  
 409          if (isset($filter['grps'])) $filter['group'] = $filter['grps'];
 410          foreach (array('user', 'name', 'mail', 'group') as $key) {
 411              if (!isset($filter[$key])) {
 412                  $filter[$key] = '%';
 413              } else {
 414                  $filter[$key] = '%' . $filter[$key] . '%';
 415              }
 416          }
 417          $filter['start'] = (int)$start;
 418          $filter['end'] = (int)$start + $limit;
 419          $filter['limit'] = (int)$limit;
 420  
 421          $result = $this->query($this->getConf('list-users'), $filter);
 422          if (!$result) return array();
 423          $users = array();
 424          if (is_array($result)) {
 425              foreach ($result as $row) {
 426                  if (!isset($row['user'])) {
 427                      $this->debugMsg("list-users statement did not return 'user' attribute", -1, __LINE__);
 428                      return array();
 429                  }
 430                  $users[] = $this->getUserData($row['user']);
 431              }
 432          } else {
 433              $this->debugMsg("list-users statement did not return a list of result", -1, __LINE__);
 434          }
 435          return $users;
 436      }
 437  
 438      /**
 439       * Return a count of the number of user which meet $filter criteria
 440       *
 441       * @param array $filter array of field/pattern pairs, empty array for no filter
 442       * @return int
 443       */
 444      public function getUserCount($filter = array())
 445      {
 446          if (is_null($filter)) $filter = array();
 447  
 448          if (isset($filter['grps'])) $filter['group'] = $filter['grps'];
 449          foreach (array('user', 'name', 'mail', 'group') as $key) {
 450              if (!isset($filter[$key])) {
 451                  $filter[$key] = '%';
 452              } else {
 453                  $filter[$key] = '%' . $filter[$key] . '%';
 454              }
 455          }
 456  
 457          $result = $this->query($this->getConf('count-users'), $filter);
 458          if (!$result || !isset($result[0]['count'])) {
 459              $this->debugMsg("Statement did not return 'count' attribute", -1, __LINE__);
 460          }
 461          return (int)$result[0]['count'];
 462      }
 463  
 464      /**
 465       * Create a new group with the given name
 466       *
 467       * @param string $group
 468       * @return bool
 469       */
 470      public function addGroup($group)
 471      {
 472          $sql = $this->getConf('insert-group');
 473  
 474          $result = $this->query($sql, array(':group' => $group));
 475          $this->clearGroupCache();
 476          if ($result === false) return false;
 477          return true;
 478      }
 479  
 480      /**
 481       * Retrieve groups
 482       *
 483       * Set getGroups capability when implemented
 484       *
 485       * @param int $start
 486       * @param int $limit
 487       * @return  array
 488       */
 489      public function retrieveGroups($start = 0, $limit = 0)
 490      {
 491          $groups = array_keys($this->selectGroups());
 492          if ($groups === false) return array();
 493  
 494          if (!$limit) {
 495              return array_splice($groups, $start);
 496          } else {
 497              return array_splice($groups, $start, $limit);
 498          }
 499      }
 500  
 501      /**
 502       * Select data of a specified user
 503       *
 504       * @param string $user the user name
 505       * @return bool|array user data, false on error
 506       */
 507      protected function selectUser($user)
 508      {
 509          $sql = $this->getConf('select-user');
 510  
 511          $result = $this->query($sql, array(':user' => $user));
 512          if (!$result) return false;
 513  
 514          if (count($result) > 1) {
 515              $this->debugMsg('Found more than one matching user', -1, __LINE__);
 516              return false;
 517          }
 518  
 519          $data = array_shift($result);
 520          $dataok = true;
 521  
 522          if (!isset($data['user'])) {
 523              $this->debugMsg("Statement did not return 'user' attribute", -1, __LINE__);
 524              $dataok = false;
 525          }
 526          if (!isset($data['hash']) && !isset($data['clear']) && !$this->checkConfig(array('check-pass'))) {
 527              $this->debugMsg("Statement did not return 'clear' or 'hash' attribute", -1, __LINE__);
 528              $dataok = false;
 529          }
 530          if (!isset($data['name'])) {
 531              $this->debugMsg("Statement did not return 'name' attribute", -1, __LINE__);
 532              $dataok = false;
 533          }
 534          if (!isset($data['mail'])) {
 535              $this->debugMsg("Statement did not return 'mail' attribute", -1, __LINE__);
 536              $dataok = false;
 537          }
 538  
 539          if (!$dataok) return false;
 540          return $data;
 541      }
 542  
 543      /**
 544       * Delete a user after removing all their group memberships
 545       *
 546       * @param string $user
 547       * @return bool true when the user was deleted
 548       */
 549      protected function deleteUser($user)
 550      {
 551          $this->pdo->beginTransaction();
 552          {
 553              $userdata = $this->getUserData($user);
 554              if ($userdata === false) goto FAIL;
 555              $allgroups = $this->selectGroups();
 556  
 557              // remove group memberships (ignore errors)
 558              foreach ($userdata['grps'] as $group) {
 559                  if (isset($allgroups[$group])) {
 560                      $this->leaveGroup($userdata, $allgroups[$group]);
 561                  }
 562              }
 563  
 564              $ok = $this->query($this->getConf('delete-user'), $userdata);
 565              if ($ok === false) goto FAIL;
 566          }
 567          $this->pdo->commit();
 568          return true;
 569  
 570          FAIL:
 571          $this->pdo->rollBack();
 572          return false;
 573      }
 574  
 575      /**
 576       * Select all groups of a user
 577       *
 578       * @param array $userdata The userdata as returned by _selectUser()
 579       * @return array|bool list of group names, false on error
 580       */
 581      protected function selectUserGroups($userdata)
 582      {
 583          global $conf;
 584          $sql = $this->getConf('select-user-groups');
 585          $result = $this->query($sql, $userdata);
 586          if ($result === false) return false;
 587  
 588          $groups = array($conf['defaultgroup']); // always add default config
 589          if (is_array($result)) {
 590              foreach ($result as $row) {
 591                  if (!isset($row['group'])) {
 592                      $this->debugMsg("No 'group' field returned in select-user-groups statement", -1, __LINE__);
 593                      return false;
 594                  }
 595                  $groups[] = $row['group'];
 596              }
 597          } else {
 598              $this->debugMsg("select-user-groups statement did not return a list of result", -1, __LINE__);
 599          }
 600  
 601          $groups = array_unique($groups);
 602          sort($groups);
 603          return $groups;
 604      }
 605  
 606      /**
 607       * Select all available groups
 608       *
 609       * @return array|bool list of all available groups and their properties
 610       */
 611      protected function selectGroups()
 612      {
 613          if ($this->groupcache) return $this->groupcache;
 614  
 615          $sql = $this->getConf('select-groups');
 616          $result = $this->query($sql);
 617          if ($result === false) return false;
 618  
 619          $groups = array();
 620          if (is_array($result)) {
 621              foreach ($result as $row) {
 622                  if (!isset($row['group'])) {
 623                      $this->debugMsg("No 'group' field returned from select-groups statement", -1, __LINE__);
 624                      return false;
 625                  }
 626  
 627                  // relayout result with group name as key
 628                  $group = $row['group'];
 629                  $groups[$group] = $row;
 630              }
 631          } else {
 632              $this->debugMsg("select-groups statement did not return a list of result", -1, __LINE__);
 633          }
 634  
 635          ksort($groups);
 636          return $groups;
 637      }
 638  
 639      /**
 640       * Remove all entries from the group cache
 641       */
 642      protected function clearGroupCache()
 643      {
 644          $this->groupcache = null;
 645      }
 646  
 647      /**
 648       * Adds the user to the group
 649       *
 650       * @param array $userdata all the user data
 651       * @param array $groupdata all the group data
 652       * @return bool
 653       */
 654      protected function joinGroup($userdata, $groupdata)
 655      {
 656          $data = array_merge($userdata, $groupdata);
 657          $sql = $this->getConf('join-group');
 658          $result = $this->query($sql, $data);
 659          if ($result === false) return false;
 660          return true;
 661      }
 662  
 663      /**
 664       * Removes the user from the group
 665       *
 666       * @param array $userdata all the user data
 667       * @param array $groupdata all the group data
 668       * @return bool
 669       */
 670      protected function leaveGroup($userdata, $groupdata)
 671      {
 672          $data = array_merge($userdata, $groupdata);
 673          $sql = $this->getConf('leave-group');
 674          $result = $this->query($sql, $data);
 675          if ($result === false) return false;
 676          return true;
 677      }
 678  
 679      /**
 680       * Executes a query
 681       *
 682       * @param string $sql The SQL statement to execute
 683       * @param array $arguments Named parameters to be used in the statement
 684       * @return array|int|bool The result as associative array for SELECTs, affected rows for others, false on error
 685       */
 686      protected function query($sql, $arguments = array())
 687      {
 688          $sql = trim($sql);
 689          if (empty($sql)) {
 690              $this->debugMsg('No SQL query given', -1, __LINE__);
 691              return false;
 692          }
 693  
 694          // execute
 695          $params = array();
 696          $sth = $this->pdo->prepare($sql);
 697          $result = false;
 698          try {
 699              // prepare parameters - we only use those that exist in the SQL
 700              foreach ($arguments as $key => $value) {
 701                  if (is_array($value)) continue;
 702                  if (is_object($value)) continue;
 703                  if ($key[0] != ':') $key = ":$key"; // prefix with colon if needed
 704                  if (strpos($sql, $key) === false) continue; // skip if parameter is missing
 705  
 706                  if (is_int($value)) {
 707                      $sth->bindValue($key, $value, PDO::PARAM_INT);
 708                  } else {
 709                      $sth->bindValue($key, $value);
 710                  }
 711                  $params[$key] = $value; //remember for debugging
 712              }
 713  
 714              $sth->execute();
 715              // only report last line's result
 716              $hasnextrowset = true;
 717              $currentsql = $sql;
 718              while ($hasnextrowset) {
 719                  if (strtolower(substr($currentsql, 0, 6)) == 'select') {
 720                      $result = $sth->fetchAll();
 721                  } else {
 722                      $result = $sth->rowCount();
 723                  }
 724                  $semi_pos = strpos($currentsql, ';');
 725                  if ($semi_pos) {
 726                      $currentsql = trim(substr($currentsql, $semi_pos + 1));
 727                  }
 728                  try {
 729                      $hasnextrowset = $sth->nextRowset(); // run next rowset
 730                  } catch (PDOException $rowset_e) {
 731                      $hasnextrowset = false; // driver does not support multi-rowset, should be executed in one time
 732                  }
 733              }
 734          } catch (Exception $e) {
 735              // report the caller's line
 736              $trace = debug_backtrace();
 737              $line = $trace[0]['line'];
 738              $dsql = $this->debugSQL($sql, $params, !defined('DOKU_UNITTEST'));
 739              $this->debugMsg($e, -1, $line);
 740              $this->debugMsg("SQL: <pre>$dsql</pre>", -1, $line);
 741          }
 742          $sth->closeCursor();
 743          $sth = null;
 744  
 745          return $result;
 746      }
 747  
 748      /**
 749       * Wrapper around msg() but outputs only when debug is enabled
 750       *
 751       * @param string|Exception $message
 752       * @param int $err
 753       * @param int $line
 754       */
 755      protected function debugMsg($message, $err = 0, $line = 0)
 756      {
 757          if (!$this->getConf('debug')) return;
 758          if (is_a($message, 'Exception')) {
 759              $err = -1;
 760              $msg = $message->getMessage();
 761              if (!$line) $line = $message->getLine();
 762          } else {
 763              $msg = $message;
 764          }
 765  
 766          if (defined('DOKU_UNITTEST')) {
 767              printf("\n%s, %s:%d\n", $msg, __FILE__, $line);
 768          } else {
 769              msg('authpdo: ' . $msg, $err, $line, __FILE__);
 770          }
 771      }
 772  
 773      /**
 774       * Check if the given config strings are set
 775       *
 776       * @param string[] $keys
 777       * @return  bool
 778       * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
 779       *
 780       */
 781      protected function checkConfig($keys)
 782      {
 783          foreach ($keys as $key) {
 784              $params = explode(':', $key);
 785              $key = array_shift($params);
 786              $sql = trim($this->getConf($key));
 787  
 788              // check if sql is set
 789              if (!$sql) return false;
 790              // check if needed params are there
 791              foreach ($params as $param) {
 792                  if (strpos($sql, ":$param") === false) return false;
 793              }
 794          }
 795  
 796          return true;
 797      }
 798  
 799      /**
 800       * create an approximation of the SQL string with parameters replaced
 801       *
 802       * @param string $sql
 803       * @param array $params
 804       * @param bool $htmlescape Should the result be escaped for output in HTML?
 805       * @return string
 806       */
 807      protected function debugSQL($sql, $params, $htmlescape = true)
 808      {
 809          foreach ($params as $key => $val) {
 810              if (is_int($val)) {
 811                  $val = $this->pdo->quote($val, PDO::PARAM_INT);
 812              } elseif (is_bool($val)) {
 813                  $val = $this->pdo->quote($val, PDO::PARAM_BOOL);
 814              } elseif (is_null($val)) {
 815                  $val = 'NULL';
 816              } else {
 817                  $val = $this->pdo->quote($val);
 818              }
 819              $sql = str_replace($key, $val, $sql);
 820          }
 821          if ($htmlescape) $sql = hsc($sql);
 822          return $sql;
 823      }
 824  }
 825  
 826  // vim:ts=4:sw=4:et: