Overview

Packages

  • alpha::controller
  • alpha::controller::front
  • alpha::exceptions
  • alpha::model
  • alpha::model::types
  • alpha::tasks
  • alpha::tests
  • alpha::util
  • alpha::util::cache
  • alpha::util::codehighlight
  • alpha::util::convertors
  • alpha::util::feeds
  • alpha::util::filters
  • alpha::util::graphs
  • alpha::util::helpers
  • alpha::util::metrics
  • alpha::util::search
  • alpha::view
  • alpha::view::renderers
  • alpha::view::widgets

Classes

  • ActionLogObject
  • AlphaDAO
  • AlphaDAOProviderFactory
  • AlphaDAOProviderMySQL
  • AlphaDAOProviderSQLite
  • ArticleCommentObject
  • ArticleObject
  • ArticleVoteObject
  • BadRequestObject
  • BlacklistedClientObject
  • BlacklistedIPObject
  • PersonObject
  • RightsObject
  • TagObject

Interfaces

  • AlphaDAOProviderInterface
  • Overview
  • Package
  • Class
  • Tree
  • Deprecated
   1: <?php
   2: 
   3: /**
   4:  * MySQL DAO provider (uses the MySQLi native API in PHP).
   5:  *
   6:  * @package alpha::model
   7:  * @since 1.1
   8:  * @author John Collins <dev@alphaframework.org>
   9:  * @version $Id: AlphaDAOProviderMySQL.inc 1810 2014-07-27 21:19:32Z alphadevx $
  10:  * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
  11:  * @copyright Copyright (c) 2014, John Collins (founder of Alpha Framework).
  12:  * All rights reserved.
  13:  *
  14:  * <pre>
  15:  * Redistribution and use in source and binary forms, with or
  16:  * without modification, are permitted provided that the
  17:  * following conditions are met:
  18:  *
  19:  * * Redistributions of source code must retain the above
  20:  *   copyright notice, this list of conditions and the
  21:  *   following disclaimer.
  22:  * * Redistributions in binary form must reproduce the above
  23:  *   copyright notice, this list of conditions and the
  24:  *   following disclaimer in the documentation and/or other
  25:  *   materials provided with the distribution.
  26:  * * Neither the name of the Alpha Framework nor the names
  27:  *   of its contributors may be used to endorse or promote
  28:  *   products derived from this software without specific
  29:  *   prior written permission.
  30:  *
  31:  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  32:  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  33:  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  34:  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  35:  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  36:  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  37:  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  38:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  39:  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  40:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  41:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
  42:  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  43:  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  44:  * </pre>
  45:  *
  46:  */
  47: class AlphaDAOProviderMySQL implements AlphaDAOProviderInterface {
  48:     /**
  49:      * Trace logger
  50:      *
  51:      * @var Logger
  52:      * @since 1.1
  53:      */
  54:     private static $logger = null;
  55: 
  56:     /**
  57:      * Datebase connection
  58:      *
  59:      * @var mysqli
  60:      * @since 1.1
  61:      */
  62:     private static $connection;
  63: 
  64:     /**
  65:      * The business object that we are mapping back to
  66:      *
  67:      * @var AlphaDAO
  68:      * @since 1.1
  69:      */
  70:     private $BO;
  71: 
  72:     /**
  73:      * The constructor
  74:      *
  75:      * @since 1.1
  76:      */
  77:     public function __construct() {
  78:         self::$logger = new Logger('AlphaDAOProviderMySQL');
  79:         self::$logger->debug('>>__construct()');
  80: 
  81:         self::$logger->debug('<<__construct');
  82:     }
  83: 
  84:     /**
  85:      * (non-PHPdoc)
  86:      * @see alpha/model/AlphaDAOProviderInterface::getConnection()
  87:      */
  88:     public static function getConnection() {
  89:         global $config;
  90: 
  91:         if (!isset(self::$connection)) {
  92:             self::$connection = new mysqli($config->get('db.hostname'), $config->get('db.username'), $config->get('db.password'), $config->get('db.name'));
  93:             self::$connection->set_charset('utf8');
  94: 
  95:             if (mysqli_connect_error()) {
  96:                 self::$logger->fatal('Could not connect to database: ['.mysqli_connect_errno().'] '.mysqli_connect_error());
  97:             }
  98:         }
  99: 
 100:         return self::$connection;
 101:     }
 102: 
 103:     /**
 104:      * (non-PHPdoc)
 105:      * @see alpha/model/AlphaDAOProviderInterface::disconnect()
 106:      */
 107:     public static function disconnect() {
 108:         if (isset(self::$connection)) {
 109:             self::$connection->close();
 110:             self::$connection = null;
 111:         }
 112:     }
 113: 
 114:     /**
 115:      * (non-PHPdoc)
 116:      * @see alpha/model/AlphaDAOProviderInterface::getLastDatabaseError()
 117:      */
 118:     public static function getLastDatabaseError() {
 119:         return self::getConnection()->error;
 120:     }
 121: 
 122:     /**
 123:      * (non-PHPdoc)
 124:      * @see alpha/model/AlphaDAOProviderInterface::query()
 125:      */
 126:     public function query($sqlQuery) {
 127:         $this->BO->setLastQuery($sqlQuery);
 128: 
 129:         $resultArray = array();
 130: 
 131:         if(!$result = self::getConnection()->query($sqlQuery)) {
 132:             throw new CustomQueryException('Failed to run the custom query, MySql error is ['.self::getConnection()->error.'], query ['.$sqlQuery.']');
 133:             return array();
 134:         }else{
 135:             while($row = $result->fetch_array(MYSQLI_ASSOC)) {
 136:                 array_push($resultArray, $row);
 137:             }
 138: 
 139:             return $resultArray;
 140:         }
 141:     }
 142: 
 143:     /**
 144:      * (non-PHPdoc)
 145:      * @see alpha/model/AlphaDAOProviderInterface::load()
 146:      */
 147:     public function load($OID) {
 148:         self::$logger->debug('>>load(OID=['.print_r($OID, true).'])');
 149: 
 150:         global $config;
 151: 
 152:         $attributes = $this->BO->getPersistentAttributes();
 153:         $fields = '';
 154:         foreach($attributes as $att)
 155:             $fields .= $att.',';
 156:         $fields = mb_substr($fields, 0, -1);
 157: 
 158:         $sqlQuery = 'SELECT '.$fields.' FROM '.$this->BO->getTableName().' WHERE OID = ? LIMIT 1;';
 159:         $this->BO->setLastQuery($sqlQuery);
 160:         $stmt = self::getConnection()->stmt_init();
 161: 
 162:         $row = array();
 163: 
 164:         if($stmt->prepare($sqlQuery)) {
 165:             $stmt->bind_param('i', $OID);
 166:             $stmt->execute();
 167: 
 168:             $result = $this->bindResult($stmt);
 169:             if(isset($result[0]))
 170:                 $row = $result[0];
 171: 
 172:             $stmt->close();
 173:         }else{
 174:             self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.'], OID is ['.print_r($OID, true).'], MySql error is ['.self::getConnection()->error.']');
 175:             if(!$this->BO->checkTableExists()) {
 176:                 $this->BO->makeTable();
 177: 
 178:                 throw new BONotFoundException('Failed to load object of OID ['.$OID.'], table ['.$this->BO->getTableName().'] did not exist so had to create!');
 179:             }
 180: 
 181:             return;
 182:         }
 183: 
 184:         if(!isset($row['OID']) || $row['OID'] < 1) {
 185:             throw new BONotFoundException('Failed to load object of OID ['.$OID.'] not found in database.');
 186:             self::$logger->debug('<<load');
 187:             return;
 188:         }
 189: 
 190:         // get the class attributes
 191:         $reflection = new ReflectionClass(get_class($this->BO));
 192:         $properties = $reflection->getProperties();
 193: 
 194:         try {
 195:             foreach($properties as $propObj) {
 196:                 $propName = $propObj->name;
 197: 
 198:                 // filter transient attributes
 199:                 if(!in_array($propName, $this->BO->getTransientAttributes())) {
 200:                     $this->BO->set($propName, $row[$propName]);
 201:                 }elseif(!$propObj->isPrivate() && $this->BO->getPropObject($propName) instanceof Relation) {
 202:                     $prop = $this->BO->getPropObject($propName);
 203: 
 204:                     // handle the setting of ONE-TO-MANY relation values
 205:                     if($prop->getRelationType() == 'ONE-TO-MANY') {
 206:                         $this->BO->set($propObj->name, $this->BO->getOID());
 207:                     }
 208: 
 209:                     // handle the setting of MANY-TO-ONE relation values
 210:                     if($prop->getRelationType() == 'MANY-TO-ONE') {
 211:                         $this->BO->set($propObj->name, $row[$propName]);
 212:                     }
 213:                 }
 214:             }
 215:         }catch (IllegalArguementException $e) {
 216:             self::$logger->warn('Bad data stored in the table ['.$this->BO->getTableName().'], field ['.$propObj->name.'] bad value['.$row[$propObj->name].'], exception ['.$e->getMessage().']');
 217:         }catch (PHPException $e) {
 218:             // it is possible that the load failed due to the table not being up-to-date
 219:             if($this->BO->checkTableNeedsUpdate()) {
 220:                 $missingFields = $this->BO->findMissingFields();
 221: 
 222:                 $count = count($missingFields);
 223: 
 224:                 for($i = 0; $i < $count; $i++)
 225:                     $this->BO->addProperty($missingFields[$i]);
 226: 
 227:                 throw new BONotFoundException('Failed to load object of OID ['.$OID.'], table ['.$this->BO->getTableName().'] was out of sync with the database so had to be updated!');
 228:                 self::$logger->warn('<<load');
 229:                 return;
 230:             }
 231:         }
 232: 
 233:         self::$logger->debug('<<load ['.$OID.']');
 234:     }
 235: 
 236:     /**
 237:      * (non-PHPdoc)
 238:      * @see alpha/model/AlphaDAOProviderInterface::loadByAttribute()
 239:      */
 240:     public function loadByAttribute($attribute, $value, $ignoreClassType=false, $loadAttributes=array()) {
 241:         self::$logger->debug('>>loadByAttribute(attribute=['.$attribute.'], value=['.$value.'], ignoreClassType=['.$ignoreClassType.'],
 242:             loadAttributes=['.var_export($loadAttributes, true).'])');
 243: 
 244:         if(count($loadAttributes) == 0)
 245:             $attributes = $this->BO->getPersistentAttributes();
 246:         else
 247:             $attributes = $loadAttributes;
 248: 
 249:         $fields = '';
 250:         foreach($attributes as $att)
 251:             $fields .= $att.',';
 252:         $fields = mb_substr($fields, 0, -1);
 253: 
 254:         if(!$ignoreClassType && $this->BO->isTableOverloaded())
 255:             $sqlQuery = 'SELECT '.$fields.' FROM '.$this->BO->getTableName().' WHERE '.$attribute.' = ? AND classname = ? LIMIT 1;';
 256:         else
 257:             $sqlQuery = 'SELECT '.$fields.' FROM '.$this->BO->getTableName().' WHERE '.$attribute.' = ? LIMIT 1;';
 258: 
 259:         self::$logger->debug('Query=['.$sqlQuery.']');
 260: 
 261:         $this->BO->setLastQuery($sqlQuery);
 262:         $stmt = self::getConnection()->stmt_init();
 263: 
 264:         $row = array();
 265: 
 266:         if($stmt->prepare($sqlQuery)) {
 267:             if($this->BO->getPropObject($attribute) instanceof Integer) {
 268:                 if(!$ignoreClassType && $this->BO->isTableOverloaded()) {
 269:                     $stmt->bind_param('is', $value, get_class($this->BO));
 270:                 }else{
 271:                     $stmt->bind_param('i', $value);
 272:                 }
 273:             }else{
 274:                 if(!$ignoreClassType && $this->BO->isTableOverloaded()) {
 275:                     $stmt->bind_param('ss', $value, get_class($this->BO));
 276:                 }else{
 277:                     $stmt->bind_param('s', $value);
 278:                 }
 279:             }
 280: 
 281:             $stmt->execute();
 282: 
 283:             $result = $this->bindResult($stmt);
 284: 
 285:             if(isset($result[0]))
 286:                 $row = $result[0];
 287: 
 288:             $stmt->close();
 289:         }else{
 290:             self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
 291:             if(!$this->BO->checkTableExists()) {
 292:                 $this->BO->makeTable();
 293: 
 294:                 throw new BONotFoundException('Failed to load object by attribute ['.$attribute.'] and value ['.$value.'], table did not exist so had to create!');
 295:             }
 296:             return;
 297:         }
 298: 
 299:         if(!isset($row['OID']) || $row['OID'] < 1) {
 300:             throw new BONotFoundException('Failed to load object by attribute ['.$attribute.'] and value ['.$value.'], not found in database.');
 301:             self::$logger->debug('<<loadByAttribute');
 302:             return;
 303:         }
 304: 
 305:         $this->OID = $row['OID'];
 306: 
 307:         // get the class attributes
 308:         $reflection = new ReflectionClass(get_class($this->BO));
 309:         $properties = $reflection->getProperties();
 310: 
 311:         try {
 312:             foreach($properties as $propObj) {
 313:                 $propName = $propObj->name;
 314: 
 315:                 if(isset($row[$propName])) {
 316:                     // filter transient attributes
 317:                     if(!in_array($propName, $this->BO->getTransientAttributes())) {
 318:                         $this->BO->set($propName, $row[$propName]);
 319:                     }elseif(!$propObj->isPrivate() && $this->BO->get($propName) != '' && $this->BO->getPropObject($propName) instanceof Relation) {
 320:                         $prop = $this->BO->getPropObject($propName);
 321: 
 322:                         // handle the setting of ONE-TO-MANY relation values
 323:                         if($prop->getRelationType() == 'ONE-TO-MANY') {
 324:                             $this->BO->set($propObj->name, $this->BO->getOID());
 325:                         }
 326:                     }
 327:                 }
 328:             }
 329:         }catch (IllegalArguementException $e) {
 330:             self::$logger->warn('Bad data stored in the table ['.$this->BO->getTableName().'], field ['.$propObj->name.'] bad value['.$row[$propObj->name].'], exception ['.$e->getMessage().']');
 331:         }catch (PHPException $e) {
 332:             // it is possible that the load failed due to the table not being up-to-date
 333:             if($this->BO->checkTableNeedsUpdate()) {
 334:                 $missingFields = $this->BO->findMissingFields();
 335: 
 336:                 $count = count($missingFields);
 337: 
 338:                 for($i = 0; $i < $count; $i++)
 339:                     $this->BO->addProperty($missingFields[$i]);
 340: 
 341:                 throw new BONotFoundException('Failed to load object by attribute ['.$attribute.'] and value ['.$value.'], table ['.$this->BO->getTableName().'] was out of sync with the database so had to be updated!');
 342:                 self::$logger->debug('<<loadByAttribute');
 343:                 return;
 344:             }
 345:         }
 346: 
 347:         self::$logger->debug('<<loadByAttribute');
 348:     }
 349: 
 350:     /**
 351:      * (non-PHPdoc)
 352:      * @see alpha/model/AlphaDAOProviderInterface::loadAll()
 353:      */
 354:     public function loadAll($start=0, $limit=0, $orderBy='OID', $order='ASC', $ignoreClassType=false) {
 355:         self::$logger->debug('>>loadAll(start=['.$start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.']');
 356: 
 357:         // ensure that the field name provided in the orderBy param is legit
 358:         try {
 359:             $field = $this->BO->get($orderBy);
 360:         }catch(AlphaException $e) {
 361:             throw new AlphaException('The field name ['.$orderBy.'] provided in the param orderBy does not exist on the class ['.get_class($this->BO).']');
 362:         }
 363: 
 364:         if(!$ignoreClassType && $this->BO->isTableOverloaded()) {
 365:             if($limit == 0) {
 366:                 $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName().' WHERE classname=\''.get_class($this->BO).'\' ORDER BY '.$orderBy.' '.$order.';';
 367:             }else{
 368:                 $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName().' WHERE classname=\''.get_class($this->BO).'\' ORDER BY '.$orderBy.' '.$order.' LIMIT '.
 369:                     $start.', '.$limit.';';
 370:             }
 371:         }else{
 372:             if($limit == 0)
 373:                 $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName().' ORDER BY '.$orderBy.' '.$order.';';
 374:             else
 375:                 $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName().' ORDER BY '.$orderBy.' '.$order.' LIMIT '.$start.', '.$limit.';';
 376:         }
 377: 
 378:         $this->BO->setLastQuery($sqlQuery);
 379: 
 380:         if(!$result = AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
 381:             throw new BONotFoundException('Failed to load object OIDs, MySql error is ['.AlphaDAOProviderMySQL::getConnection()->error.'], query ['.$this->BO->getLastQuery().']');
 382:             self::$logger->debug('<<loadAll [0]');
 383:             return array();
 384:         }
 385: 
 386:         // now build an array of objects to be returned
 387:         $objects = array();
 388:         $count = 0;
 389:         $BO_Class = get_class($this->BO);
 390: 
 391:         while($row = $result->fetch_array(MYSQLI_ASSOC)) {
 392:             try {
 393:                 $obj = new $BO_Class();
 394:                 $obj->load($row['OID']);
 395:                 $objects[$count] = $obj;
 396:                 $count++;
 397:             }catch(ResourceNotAllowedException $e) {
 398:                 // the resource not allowed will be absent from the list
 399:             }
 400:         }
 401: 
 402:         self::$logger->debug('<<loadAll ['.count($objects).']');
 403:         return $objects;
 404:     }
 405: 
 406:     /**
 407:      * (non-PHPdoc)
 408:      * @see alpha/model/AlphaDAOProviderInterface::loadAllByAttribute()
 409:      */
 410:     public function loadAllByAttribute($attribute, $value, $start=0, $limit=0, $orderBy="OID", $order="ASC", $ignoreClassType=false, $constructorArgs=array()) {
 411:         self::$logger->debug('>>loadAllByAttribute(attribute=['.$attribute.'], value=['.$value.'], start=['.$start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.'], constructorArgs=['.print_r($constructorArgs, true).']');
 412: 
 413:         if ($limit != 0)
 414:             $limit = ' LIMIT '.$start.', '.$limit.';';
 415:         else
 416:             $limit = ';';
 417: 
 418:         if(!$ignoreClassType && $this->BO->isTableOverloaded())
 419:             $sqlQuery = "SELECT OID FROM ".$this->BO->getTableName()." WHERE $attribute = ? AND classname = ? ORDER BY ".$orderBy." ".$order.$limit;
 420:         else
 421:             $sqlQuery = "SELECT OID FROM ".$this->BO->getTableName()." WHERE $attribute = ? ORDER BY ".$orderBy." ".$order.$limit;
 422: 
 423:         $this->BO->setLastQuery($sqlQuery);
 424:         self::$logger->debug($sqlQuery);
 425: 
 426:         $stmt = AlphaDAOProviderMySQL::getConnection()->stmt_init();
 427: 
 428:         $row = array();
 429: 
 430:         if($stmt->prepare($sqlQuery)) {
 431:             if($this->BO->getPropObject($attribute) instanceof Integer) {
 432:                 if($this->BO->isTableOverloaded()) {
 433:                     $stmt->bind_param('is', $value, get_class($this->BO));
 434:                 }else{
 435:                     $stmt->bind_param('i', $value);
 436:                 }
 437:             }else{
 438:                 if($this->BO->isTableOverloaded()) {
 439:                     $stmt->bind_param('ss', $value, get_class($this->BO));
 440:                 }else{
 441:                     $stmt->bind_param('s', $value);
 442:                 }
 443:             }
 444: 
 445:             $stmt->execute();
 446: 
 447:             $result = $this->bindResult($stmt);
 448: 
 449:             $stmt->close();
 450:         }else{
 451:             self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
 452:             if(!$this->BO->checkTableExists()) {
 453:                 $this->BO->makeTable();
 454: 
 455:                 throw new BONotFoundException('Failed to load objects by attribute ['.$attribute.'] and value ['.$value.'], table did not exist so had to create!');
 456:             }
 457:             self::$logger->debug('<<loadAllByAttribute []');
 458:             return array();
 459:         }
 460: 
 461:         // now build an array of objects to be returned
 462:         $objects = array();
 463:         $count = 0;
 464:         $BO_Class = get_class($this->BO);
 465: 
 466:         foreach($result as $row) {
 467:             try {
 468:                 $argsCount = count($constructorArgs);
 469: 
 470:                 if($argsCount < 1) {
 471:                     $obj = new $BO_Class();
 472:                 }else{
 473:                     switch ($argsCount) {
 474:                         case 1:
 475:                             $obj = new $BO_Class($constructorArgs[0]);
 476:                         break;
 477:                         case 2:
 478:                             $obj = new $BO_Class($constructorArgs[0],$constructorArgs[1]);
 479:                         break;
 480:                         case 3:
 481:                             $obj = new $BO_Class($constructorArgs[0],$constructorArgs[1],$constructorArgs[2]);
 482:                         break;
 483:                         case 4:
 484:                             $obj = new $BO_Class($constructorArgs[0],$constructorArgs[1],$constructorArgs[2],$constructorArgs[3]);
 485:                         break;
 486:                         case 5:
 487:                             $obj = new $BO_Class($constructorArgs[0],$constructorArgs[1],$constructorArgs[2],$constructorArgs[3],$constructorArgs[4]);
 488:                         break;
 489:                         default:
 490:                             throw new IllegalArguementException('Too many elements in the $constructorArgs array passed to the loadAllByAttribute method!');
 491:                         break;
 492:                     }
 493:                 }
 494: 
 495:                 $obj->load($row['OID']);
 496:                 $objects[$count] = $obj;
 497:                 $count++;
 498:             }catch(ResourceNotAllowedException $e) {
 499:                 // the resource not allowed will be absent from the list
 500:             }
 501:         }
 502: 
 503:         self::$logger->debug('<<loadAllByAttribute ['.count($objects).']');
 504:         return $objects;
 505:     }
 506: 
 507:     /**
 508:      * (non-PHPdoc)
 509:      * @see alpha/model/AlphaDAOProviderInterface::loadAllByAttributes()
 510:      */
 511:     public function loadAllByAttributes($attributes=array(), $values=array(), $start=0, $limit=0, $orderBy='OID', $order='ASC', $ignoreClassType=false, $constructorArgs=array()) {
 512:         self::$logger->debug('>>loadAllByAttributes(attributes=['.var_export($attributes, true).'], values=['.var_export($values, true).'], start=['.
 513:             $start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.'], constructorArgs=['.print_r($constructorArgs, true).']');
 514: 
 515:         $whereClause = ' WHERE';
 516: 
 517:         $count = count($attributes);
 518: 
 519:         for($i = 0; $i < $count; $i++) {
 520:             $whereClause .= ' '.$attributes[$i].' = ? AND';
 521:             self::$logger->debug($whereClause);
 522:         }
 523: 
 524:         if(!$ignoreClassType && $this->BO->isTableOverloaded())
 525:             $whereClause .= ' classname = ? AND';
 526: 
 527:         // remove the last " AND"
 528:         $whereClause = mb_substr($whereClause, 0, -4);
 529: 
 530:         if ($limit != 0)
 531:             $limit = ' LIMIT '.$start.', '.$limit.';';
 532:         else
 533:             $limit = ';';
 534: 
 535:         $sqlQuery = "SELECT OID FROM ".$this->BO->getTableName().$whereClause." ORDER BY ".$orderBy." ".$order.$limit;
 536: 
 537:         $this->BO->setLastQuery($sqlQuery);
 538: 
 539:         $stmt = AlphaDAOProviderMySQL::getConnection()->stmt_init();
 540: 
 541:         if($stmt->prepare($sqlQuery)) {
 542:             // bind params where required attributes are provided
 543:             if(count($attributes) > 0 && count($attributes) == count($values)) {
 544:                 $stmt = $this->bindParams($stmt, $attributes, $values);
 545:             }else{
 546:                 // we'll still need to bind the "classname" for overloaded BOs...
 547:                 if($this->BO->isTableOverloaded())
 548:                     $stmt->bind_param('s', get_class($this->BO));
 549:             }
 550:             $stmt->execute();
 551: 
 552:             $result = $this->bindResult($stmt);
 553: 
 554:             $stmt->close();
 555:         }else{
 556:             self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
 557: 
 558:             if(!$this->BO->checkTableExists()) {
 559:                 $this->BO->makeTable();
 560: 
 561:                 throw new BONotFoundException('Failed to load objects by attributes ['.var_export($attributes, true).'] and values ['.
 562:                     var_export($values, true).'], table did not exist so had to create!');
 563:             }
 564: 
 565:             self::$logger->debug('<<loadAllByAttributes []');
 566:             return array();
 567:         }
 568: 
 569:         // now build an array of objects to be returned
 570:         $objects = array();
 571:         $count = 0;
 572:         $BO_Class = get_class($this->BO);
 573: 
 574:         foreach($result as $row) {
 575:             try {
 576:                 $argsCount = count($constructorArgs);
 577: 
 578:                 if($argsCount < 1) {
 579:                     $obj = new $BO_Class();
 580:                 }else{
 581:                     switch ($argsCount) {
 582:                         case 1:
 583:                             $obj = new $BO_Class($constructorArgs[0]);
 584:                         break;
 585:                         case 2:
 586:                             $obj = new $BO_Class($constructorArgs[0],$constructorArgs[1]);
 587:                         break;
 588:                         case 3:
 589:                             $obj = new $BO_Class($constructorArgs[0],$constructorArgs[1],$constructorArgs[2]);
 590:                         break;
 591:                         case 4:
 592:                             $obj = new $BO_Class($constructorArgs[0],$constructorArgs[1],$constructorArgs[2],$constructorArgs[3]);
 593:                         break;
 594:                         case 5:
 595:                             $obj = new $BO_Class($constructorArgs[0],$constructorArgs[1],$constructorArgs[2],$constructorArgs[3],$constructorArgs[4]);
 596:                         break;
 597:                         default:
 598:                             throw new IllegalArguementException('Too many elements in the $constructorArgs array passed to the loadAllByAttribute method!');
 599:                         break;
 600:                     }
 601:                 }
 602: 
 603:                 $obj->load($row['OID']);
 604:                 $objects[$count] = $obj;
 605:                 $count++;
 606:             }catch(ResourceNotAllowedException $e) {
 607:                 // the resource not allowed will be absent from the list
 608:             }
 609:         }
 610: 
 611:         self::$logger->debug('<<loadAllByAttributes ['.count($objects).']');
 612:         return $objects;
 613:     }
 614: 
 615:     /**
 616:      * (non-PHPdoc)
 617:      * @see alpha/model/AlphaDAOProviderInterface::loadAllByDayUpdated()
 618:      */
 619:     public function loadAllByDayUpdated($date, $start=0, $limit=0, $orderBy="OID", $order="ASC", $ignoreClassType=false) {
 620:         self::$logger->debug('>>loadAllByDayUpdated(date=['.$date.'], start=['.$start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.']');
 621: 
 622:         if ($start != 0 && $limit != 0)
 623:             $limit = ' LIMIT '.$start.', '.$limit.';';
 624:         else
 625:             $limit = ';';
 626: 
 627:         if(!$ignoreClassType && $this->BO->isTableOverloaded())
 628:             $sqlQuery = "SELECT OID FROM ".$this->BO->getTableName()." WHERE updated_ts >= '".$date." 00:00:00' AND updated_ts <= '".$date." 23:59:59' AND classname='".get_class($this->BO)."' ORDER BY ".$orderBy." ".$order.$limit;
 629:         else
 630:             $sqlQuery = "SELECT OID FROM ".$this->BO->getTableName()." WHERE updated_ts >= '".$date." 00:00:00' AND updated_ts <= '".$date." 23:59:59' ORDER BY ".$orderBy." ".$order.$limit;
 631: 
 632:         $this->BO->setLastQuery($sqlQuery);
 633: 
 634:         if(!$result = AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
 635:             throw new BONotFoundException('Failed to load object OIDs, MySql error is ['.self::getConnection()->error.'], query ['.$this->BO->getLastQuery().']');
 636:             self::$logger->debug('<<loadAllByDayUpdated []');
 637:             return array();
 638:         }
 639: 
 640:         // now build an array of objects to be returned
 641:         $objects = array();
 642:         $count = 0;
 643:         $BO_Class = get_class($this->BO);
 644: 
 645:         while($row = $result->fetch_array(MYSQLI_ASSOC)) {
 646:             $obj = new $BO_Class();
 647:             $obj->load($row['OID']);
 648:             $objects[$count] = $obj;
 649:             $count++;
 650:         }
 651: 
 652:         self::$logger->debug('<<loadAllByDayUpdated ['.count($objects).']');
 653:         return $objects;
 654:     }
 655: 
 656:     /**
 657:      * (non-PHPdoc)
 658:      * @see alpha/model/AlphaDAOProviderInterface::loadAllFieldValuesByAttribute()
 659:      */
 660:     public function loadAllFieldValuesByAttribute($attribute, $value, $returnAttribute, $order='ASC', $ignoreClassType=false) {
 661:         self::$logger->debug('>>loadAllFieldValuesByAttribute(attribute=['.$attribute.'], value=['.$value.'], returnAttribute=['.$returnAttribute.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.']');
 662: 
 663:         if(!$ignoreClassType && $this->BO->isTableOverloaded())
 664:             $sqlQuery = "SELECT ".$returnAttribute." FROM ".$this->BO->getTableName()." WHERE $attribute = '$value' AND classname='".get_class($this->BO)."' ORDER BY OID ".$order.";";
 665:         else
 666:             $sqlQuery = "SELECT ".$returnAttribute." FROM ".$this->BO->getTableName()." WHERE $attribute = '$value' ORDER BY OID ".$order.";";
 667: 
 668:         $this->BO->setLastQuery($sqlQuery);
 669: 
 670:         self::$logger->debug('lastQuery ['.$sqlQuery.']');
 671: 
 672:         if(!$result = AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
 673:             throw new BONotFoundException('Failed to load field ['.$returnAttribute.'] values, MySql error is ['.AlphaDAOProviderMySQL::getConnection()->error.'], query ['.$this->getLastQuery().']');
 674:             self::$logger->debug('<<loadAllFieldValuesByAttribute []');
 675:             return array();
 676:         }
 677: 
 678:         // now build an array of attribute values to be returned
 679:         $values = array();
 680:         $count = 0;
 681:         $BO_Class = get_class($this->BO);
 682: 
 683:         while($row = $result->fetch_array(MYSQLI_ASSOC)) {
 684:             $values[$count] = $row[$returnAttribute];
 685:             $count++;
 686:         }
 687: 
 688:         self::$logger->debug('<<loadAllFieldValuesByAttribute ['.count($values).']');
 689:         return $values;
 690:     }
 691: 
 692:     /**
 693:      * (non-PHPdoc)
 694:      * @see alpha/model/AlphaDAOProviderInterface::save()
 695:      */
 696:     public function save() {
 697:         self::$logger->debug('>>save()');
 698: 
 699:         // get the class attributes
 700:         $reflection = new ReflectionClass(get_class($this->BO));
 701:         $properties = $reflection->getProperties();
 702:         $sqlQuery = '';
 703:         $stmt = null;
 704: 
 705:         if($this->BO->getVersion() != $this->BO->getVersionNumber()->getValue()){
 706:             throw new LockingException('Could not save the object as it has been updated by another user.  Please try saving again.');
 707:             return;
 708:         }
 709: 
 710:         // set the "updated by" fields, we can only set the user id if someone is logged in
 711:         if(isset($_SESSION['currentUser']))
 712:             $this->BO->set('updated_by', $_SESSION['currentUser']->getOID());
 713: 
 714:         $this->BO->set('updated_ts', new Timestamp(date("Y-m-d H:i:s")));
 715: 
 716:         // check to see if it is a transient object that needs to be inserted
 717:         if($this->BO->isTransient()) {
 718:             $savedFieldsCount = 0;
 719:             $sqlQuery = 'INSERT INTO '.$this->BO->getTableName().' (';
 720: 
 721:             foreach($properties as $propObj) {
 722:                 $propName = $propObj->name;
 723:                 if (!in_array($propName, $this->BO->getTransientAttributes())) {
 724:                     // Skip the OID, database auto number takes care of this.
 725:                     if($propName != 'OID' && $propName != 'version_num') {
 726:                         $sqlQuery .= "$propName,";
 727:                         $savedFieldsCount++;
 728:                     }
 729: 
 730:                     if($propName == 'version_num') {
 731:                         $sqlQuery .= 'version_num,';
 732:                         $savedFieldsCount++;
 733:                     }
 734:                 }
 735:             }
 736:             if($this->BO->isTableOverloaded())
 737:                 $sqlQuery .= 'classname,';
 738: 
 739:             $sqlQuery = rtrim($sqlQuery, ",");
 740: 
 741:             $sqlQuery .= ') VALUES (';
 742: 
 743:             for($i = 0; $i < $savedFieldsCount; $i++)
 744:                 $sqlQuery.= '?,';
 745: 
 746:             if($this->BO->isTableOverloaded())
 747:                 $sqlQuery.= '?,';
 748: 
 749:             $sqlQuery = rtrim($sqlQuery, ',').')';
 750: 
 751:             $this->BO->setLastQuery($sqlQuery);
 752:             self::$logger->debug('Query ['.$sqlQuery.']');
 753: 
 754:             $stmt = AlphaDAOProviderMySQL::getConnection()->stmt_init();
 755: 
 756:             if($stmt->prepare($sqlQuery)) {
 757:                 $stmt = $this->bindParams($stmt);
 758:                 $stmt->execute();
 759:             }else{
 760:                 throw new FailedSaveException('Failed to save object, error is ['.$stmt->error.'], query ['.$this->BO->getLastQuery().']');
 761:             }
 762:         }else{
 763:             // assume that it is a persistent object that needs to be updated
 764:             $savedFieldsCount = 0;
 765:             $sqlQuery = 'UPDATE '.$this->BO->getTableName().' SET ';
 766: 
 767:             foreach($properties as $propObj) {
 768:                 $propName = $propObj->name;
 769:                 if (!in_array($propName, $this->BO->getTransientAttributes())) {
 770:                     // Skip the OID, database auto number takes care of this.
 771:                     if($propName != 'OID' && $propName != 'version_num') {
 772:                         $sqlQuery .= "$propName = ?,";
 773:                         $savedFieldsCount++;
 774:                     }
 775: 
 776:                     if($propName == 'version_num') {
 777:                         $sqlQuery .= 'version_num = ?,';
 778:                         $savedFieldsCount++;
 779:                     }
 780:                 }
 781:             }
 782: 
 783:             if($this->BO->isTableOverloaded())
 784:                 $sqlQuery .= 'classname = ?,';
 785: 
 786:             $sqlQuery = rtrim($sqlQuery, ",");
 787: 
 788:             $sqlQuery .= " WHERE OID=?;";
 789: 
 790:             $this->BO->setLastQuery($sqlQuery);
 791:             $stmt = AlphaDAOProviderMySQL::getConnection()->stmt_init();
 792: 
 793:             if($stmt->prepare($sqlQuery)) {
 794:                 $this->bindParams($stmt);
 795:                 $stmt->execute();
 796:             }else{
 797:                 throw new FailedSaveException('Failed to save object, error is ['.$stmt->error.'], query ['.$this->BO->getLastQuery().']');
 798:             }
 799:         }
 800: 
 801:         if ($stmt != null && $stmt->error == '') {
 802:             // populate the updated OID in case we just done an insert
 803:             if($this->BO->isTransient())
 804:                 $this->BO->setOID(AlphaDAOProviderMySQL::getConnection()->insert_id);
 805: 
 806:             try {
 807:                 foreach($properties as $propObj) {
 808:                     $propName = $propObj->name;
 809: 
 810:                     if($this->BO->getPropObject($propName) instanceof Relation) {
 811:                         $prop = $this->BO->getPropObject($propName);
 812: 
 813:                         // handle the saving of MANY-TO-MANY relation values
 814:                         if($prop->getRelationType() == 'MANY-TO-MANY' && isset($_POST[$propName]) && $_POST[$propName] != '00000000000') {
 815:                             try {
 816:                                 try{
 817:                                     // check to see if the rel is on this class
 818:                                     $side = $prop->getSide(get_class($this->BO));
 819:                                 }catch (IllegalArguementException $iae) {
 820:                                     $side = $prop->getSide(ucfirst($this->BO->getTableName()).'Object');
 821:                                 }
 822: 
 823:                                 $lookUp = $prop->getLookup();
 824: 
 825:                                 // first delete all of the old RelationLookup objects for this rel
 826:                                 try {
 827:                                     if($side == 'left')
 828:                                         $lookUp->deleteAllByAttribute('leftID', $this->BO->getOID());
 829:                                     else
 830:                                         $lookUp->deleteAllByAttribute('rightID', $this->BO->getOID());
 831:                                 }catch (Exception $e) {
 832:                                     throw new FailedSaveException('Failed to delete old RelationLookup objects on the table ['.$prop->getLookup()->getTableName().'], error is ['.$e->getMessage().']');
 833:                                 }
 834: 
 835:                                 $OIDs = explode(',', $_POST[$propName]);
 836: 
 837:                                 if(isset($OIDs) && !empty($OIDs[0])) {
 838:                                     // now for each posted OID, create a new RelationLookup record and save
 839:                                     foreach ($OIDs as $oid) {
 840:                                         $newLookUp = new RelationLookup($lookUp->get('leftClassName'), $lookUp->get('rightClassName'));
 841:                                         if($side == 'left') {
 842:                                             $newLookUp->set('leftID', $this->BO->getOID());
 843:                                             $newLookUp->set('rightID', $oid);
 844:                                         }else{
 845:                                             $newLookUp->set('rightID', $this->BO->getOID());
 846:                                             $newLookUp->set('leftID', $oid);
 847:                                         }
 848:                                         $newLookUp->save();
 849:                                     }
 850:                                 }
 851:                             }catch (Exception $e) {
 852:                                 throw new FailedSaveException('Failed to update a MANY-TO-MANY relation on the object, error is ['.$e->getMessage().']');
 853:                                 return;
 854:                             }
 855:                         }
 856: 
 857:                         // handle the saving of ONE-TO-MANY relation values
 858:                         if($prop->getRelationType() == 'ONE-TO-MANY') {
 859:                             $prop->setValue($this->BO->getOID());
 860:                         }
 861:                     }
 862:                 }
 863:             }catch (Exception $e) {
 864:                 throw new FailedSaveException('Failed to save object, error is ['.$e->getMessage().']');
 865:                 return;
 866:             }
 867: 
 868:             $stmt->close();
 869:         }else{
 870:             // there has been an error, so decrement the version number back
 871:             $temp = $this->BO->getVersionNumber()->getValue();
 872:             $this->BO->set('version_num', $temp-1);
 873: 
 874:             // check for unique violations
 875:             if(AlphaDAOProviderMySQL::getConnection()->errno == '1062') {
 876:                 throw new ValidationException('Failed to save, the value '.$this->findOffendingValue(AlphaDAOProviderMySQL::getConnection()->error).' is already in use!');
 877:                 return;
 878:             }else{
 879:                 throw new FailedSaveException('Failed to save object, MySql error is ['.AlphaDAOProviderMySQL::getConnection()->error.'], query ['.$this->BO->getLastQuery().']');
 880:             }
 881:         }
 882: 
 883:         if($this->BO->getMaintainHistory())
 884:             $this->BO->saveHistory();
 885:     }
 886: 
 887:     /**
 888:      * (non-PHPdoc)
 889:      * @see alpha/model/AlphaDAOProviderInterface::saveAttribute()
 890:      */
 891:     public function saveAttribute($attribute, $value) {
 892:         self::$logger->debug('>>saveAttribute(attribute=['.$attribute.'], value=['.$value.'])');
 893: 
 894:         // assume that it is a persistent object that needs to be updated
 895:         $sqlQuery = 'UPDATE '.$this->BO->getTableName().' SET '.$attribute.'=?, version_num = ? WHERE OID=?;';
 896: 
 897:         $this->BO->setLastQuery($sqlQuery);
 898:         $stmt = self::getConnection()->stmt_init();
 899: 
 900:         $newVersionNumber = $this->BO->getVersionNumber()->getValue()+1;
 901: 
 902:         if($stmt->prepare($sqlQuery)) {
 903:             if($this->BO->getPropObject($attribute) instanceof Integer)
 904:                 $bindingsType = 'i';
 905:             else
 906:                 $bindingsType = 's';
 907:             $stmt->bind_param($bindingsType.'ii', $value, $newVersionNumber, $this->BO->getOID());
 908:             self::$logger->debug('Binding params ['.$bindingsType.'i, '.$value.', '.$this->BO->getOID().']');
 909:             $stmt->execute();
 910:         }else{
 911:             throw new FailedSaveException('Failed to save attribute, error is ['.$stmt->error.'], query ['.$this->BO->getLastQuery().']');
 912:         }
 913: 
 914:         $stmt->close();
 915: 
 916:         $this->BO->set($attribute, $value);
 917:         $this->BO->set('version_num', $newVersionNumber);
 918: 
 919:         if($this->BO->getMaintainHistory())
 920:             $this->BO->saveHistory();
 921: 
 922:         self::$logger->debug('<<saveAttribute');
 923:     }
 924: 
 925:     /**
 926:      * (non-PHPdoc)
 927:      * @see alpha/model/AlphaDAOProviderInterface::saveHistory()
 928:      */
 929:     public function saveHistory() {
 930:         self::$logger->debug('>>saveHistory()');
 931: 
 932:         // get the class attributes
 933:         $reflection = new ReflectionClass(get_class($this->BO));
 934:         $properties = $reflection->getProperties();
 935:         $sqlQuery = '';
 936:         $stmt = null;
 937: 
 938:         $savedFieldsCount = 0;
 939:         $attributeNames = array();
 940:         $attributeValues = array();
 941: 
 942:         $sqlQuery = 'INSERT INTO '.$this->BO->getTableName().'_history (';
 943: 
 944:         foreach($properties as $propObj) {
 945:             $propName = $propObj->name;
 946:             if (!in_array($propName, $this->BO->getTransientAttributes())) {
 947:                 $sqlQuery .= "$propName,";
 948:                 $attributeNames[] = $propName;
 949:                 $attributeValues[] = $this->BO->get($propName);
 950:                 $savedFieldsCount++;
 951:             }
 952:         }
 953: 
 954:         if($this->BO->isTableOverloaded())
 955:             $sqlQuery .= 'classname,';
 956: 
 957:         $sqlQuery = rtrim($sqlQuery, ",");
 958: 
 959:         $sqlQuery .= ') VALUES (';
 960: 
 961:         for($i = 0; $i < $savedFieldsCount; $i++)
 962:             $sqlQuery.= '?,';
 963: 
 964:         if($this->BO->isTableOverloaded())
 965:             $sqlQuery.= '?,';
 966: 
 967:         $sqlQuery = rtrim($sqlQuery, ',').')';
 968: 
 969:         $this->BO->setLastQuery($sqlQuery);
 970:         self::$logger->debug('Query ['.$sqlQuery.']');
 971: 
 972:         $stmt = AlphaDAOProviderMySQL::getConnection()->stmt_init();
 973: 
 974:         if($stmt->prepare($sqlQuery)) {
 975:             $stmt = $this->bindParams($stmt, $attributeNames, $attributeValues);
 976:             $stmt->execute();
 977:         }else{
 978:             throw new FailedSaveException('Failed to save object history, error is ['.$stmt->error.'], query ['.$this->BO->getLastQuery().']');
 979:         }
 980:     }
 981: 
 982:     /**
 983:      * (non-PHPdoc)
 984:      * @see alpha/model/AlphaDAOProviderInterface::delete()
 985:      */
 986:     public function delete() {
 987:         self::$logger->debug('>>delete()');
 988: 
 989:         $sqlQuery = "DELETE FROM ".$this->BO->getTableName()." WHERE OID = ?;";
 990: 
 991:         $this->BO->setLastQuery($sqlQuery);
 992: 
 993:         $stmt = self::getConnection()->stmt_init();
 994: 
 995:         if($stmt->prepare($sqlQuery)) {
 996:             $stmt->bind_param('i', $this->BO->getOID());
 997:             $stmt->execute();
 998:             self::$logger->debug('Deleted the object ['.$this->BO->getOID().'] of class ['.get_class($this->BO).']');
 999:         }else{
1000:             throw new FailedDeleteException('Failed to delete object ['.$this->BO->getOID().'], error is ['.$stmt->error.'], query ['.$this->BO->getLastQuery().']');
1001:         }
1002: 
1003:         $stmt->close();
1004: 
1005:         self::$logger->debug('<<delete');
1006:     }
1007: 
1008:     /**
1009:      * (non-PHPdoc)
1010:      * @see alpha/model/AlphaDAOProviderInterface::getVersion()
1011:      */
1012:     public function getVersion() {
1013:         self::$logger->debug('>>getVersion()');
1014: 
1015:         $sqlQuery = 'SELECT version_num FROM '.$this->BO->getTableName().' WHERE OID = ?;';
1016:         $this->BO->setLastQuery($sqlQuery);
1017: 
1018:         $stmt = AlphaDAOProviderMySQL::getConnection()->stmt_init();
1019: 
1020:         if($stmt->prepare($sqlQuery)) {
1021:             $stmt->bind_param('i', $this->BO->getOID());
1022: 
1023:             $stmt->execute();
1024: 
1025:             $result = $this->bindResult($stmt);
1026:             if(isset($result[0]))
1027:                 $row = $result[0];
1028: 
1029:             $stmt->close();
1030:         }else{
1031:             self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
1032:             if(!$this->BO->checkTableExists()) {
1033:                 $this->BO->makeTable();
1034: 
1035:                 throw new BONotFoundException('Failed to get the version number, table did not exist so had to create!');
1036:             }
1037:             return;
1038:         }
1039: 
1040:         if(!isset($row['version_num']) || $row['version_num'] < 1) {
1041:             self::$logger->debug('<<getVersion [0]');
1042:             return 0;
1043:         }else{
1044:             $version_num = $row['version_num'];
1045: 
1046:             self::$logger->debug('<<getVersion ['.$version_num.']');
1047:             return $version_num;
1048:         }
1049:     }
1050: 
1051:     /**
1052:      * (non-PHPdoc)
1053:      * @see alpha/model/AlphaDAOProviderInterface::makeTable()
1054:      */
1055:     public function makeTable() {
1056:         self::$logger->debug('>>makeTable()');
1057: 
1058:         $sqlQuery = "CREATE TABLE ".$this->BO->getTableName()." (OID INT(11) ZEROFILL NOT NULL AUTO_INCREMENT,";
1059: 
1060:         // get the class attributes
1061:         $reflection = new ReflectionClass(get_class($this->BO));
1062:         $properties = $reflection->getProperties();
1063: 
1064:         foreach($properties as $propObj) {
1065:             $propName = $propObj->name;
1066: 
1067:             if(!in_array($propName, $this->BO->getTransientAttributes()) && $propName != "OID") {
1068:                 $propClass = get_class($this->BO->getPropObject($propName));
1069: 
1070:                 switch (mb_strtoupper($propClass)) {
1071:                     case "INTEGER":
1072:                         // special properties for RelationLookup OIDs
1073:                         if($this->BO instanceof RelationLookup && ($propName == 'leftID' || $propName == 'rightID'))
1074:                             $sqlQuery .= "$propName INT(".$this->BO->getPropObject($propName)->getSize().") ZEROFILL NOT NULL,";
1075:                         else
1076:                             $sqlQuery .= "$propName INT(".$this->BO->getPropObject($propName)->getSize()."),";
1077:                     break;
1078:                     case "DOUBLE":
1079:                         $sqlQuery .= "$propName DOUBLE(".$this->BO->getPropObject($propName)->getSize(true)."),";
1080:                     break;
1081:                     case "STRING":
1082:                         $sqlQuery .= "$propName VARCHAR(".$this->BO->getPropObject($propName)->getSize().") CHARACTER SET utf8,";
1083:                     break;
1084:                     case "TEXT":
1085:                         $sqlQuery .= "$propName TEXT CHARACTER SET utf8,";
1086:                     break;
1087:                     case "BOOLEAN":
1088:                         $sqlQuery .= "$propName CHAR(1) DEFAULT '0',";
1089:                     break;
1090:                     case "DATE":
1091:                         $sqlQuery .= "$propName DATE,";
1092:                     break;
1093:                     case "TIMESTAMP":
1094:                         $sqlQuery .= "$propName DATETIME,";
1095:                     break;
1096:                     case "ENUM":
1097:                         $sqlQuery .= "$propName ENUM(";
1098:                         $enumVals = $this->BO->getPropObject($propName)->getOptions();
1099:                         foreach($enumVals as $val) {
1100:                             $sqlQuery .= "'".$val."',";
1101:                         }
1102:                         $sqlQuery = rtrim($sqlQuery, ",");
1103:                         $sqlQuery .= ") CHARACTER SET utf8,";
1104:                     break;
1105:                     case "DENUM":
1106:                         $tmp = new DEnum(get_class($this->BO).'::'.$propName);
1107:                         $sqlQuery .= "$propName INT(11) ZEROFILL,";
1108:                     break;
1109:                     case "RELATION":
1110:                         $sqlQuery .= "$propName INT(11) ZEROFILL UNSIGNED,";
1111:                     break;
1112:                     default:
1113:                         $sqlQuery .= "";
1114:                     break;
1115:                 }
1116:             }
1117:         }
1118:         if($this->BO->isTableOverloaded())
1119:             $sqlQuery .= "classname VARCHAR(100),";
1120: 
1121:         $sqlQuery .= "PRIMARY KEY (OID)) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;";
1122: 
1123:         $this->BO->setLastQuery($sqlQuery);
1124: 
1125:         if(!$result = AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
1126:             throw new AlphaException('Failed to create the table ['.$this->BO->getTableName().'] for the class ['.get_class($this->BO).'], database error is ['.AlphaDAOProviderMySQL::getConnection()->error.']');
1127:             self::$logger->debug('<<makeTable');
1128:         }
1129: 
1130:         // check the table indexes if any additional ones required
1131:         $this->checkIndexes();
1132: 
1133:         if($this->BO->getMaintainHistory())
1134:             $this->BO->makeHistoryTable();
1135: 
1136:         self::$logger->debug('<<makeTable');
1137:     }
1138: 
1139:     /**
1140:      * (non-PHPdoc)
1141:      * @see alpha/model/AlphaDAOProviderInterface::makeHistoryTable()
1142:      */
1143:     public function makeHistoryTable() {
1144:         self::$logger->debug('>>makeHistoryTable()');
1145: 
1146:         $sqlQuery = "CREATE TABLE ".$this->BO->getTableName()."_history (OID INT(11) ZEROFILL NOT NULL,";
1147: 
1148:         // get the class attributes
1149:         $reflection = new ReflectionClass(get_class($this->BO));
1150:         $properties = $reflection->getProperties();
1151: 
1152:         foreach($properties as $propObj) {
1153:             $propName = $propObj->name;
1154: 
1155:             if(!in_array($propName, $this->BO->getTransientAttributes()) && $propName != "OID") {
1156:                 $propClass = get_class($this->BO->getPropObject($propName));
1157: 
1158:                 switch (mb_strtoupper($propClass)) {
1159:                     case "INTEGER":
1160:                         // special properties for RelationLookup OIDs
1161:                         if($this->BO instanceof RelationLookup && ($propName == 'leftID' || $propName == 'rightID'))
1162:                             $sqlQuery .= "$propName INT(".$this->BO->getPropObject($propName)->getSize().") ZEROFILL NOT NULL,";
1163:                         else
1164:                             $sqlQuery .= "$propName INT(".$this->BO->getPropObject($propName)->getSize()."),";
1165:                     break;
1166:                     case "DOUBLE":
1167:                         $sqlQuery .= "$propName DOUBLE(".$this->BO->getPropObject($propName)->getSize(true)."),";
1168:                     break;
1169:                     case "STRING":
1170:                         $sqlQuery .= "$propName VARCHAR(".$this->BO->getPropObject($propName)->getSize()."),";
1171:                     break;
1172:                     case "TEXT":
1173:                         $sqlQuery .= "$propName TEXT,";
1174:                     break;
1175:                     case "BOOLEAN":
1176:                         $sqlQuery .= "$propName CHAR(1) DEFAULT '0',";
1177:                     break;
1178:                     case "DATE":
1179:                         $sqlQuery .= "$propName DATE,";
1180:                     break;
1181:                     case "TIMESTAMP":
1182:                         $sqlQuery .= "$propName DATETIME,";
1183:                     break;
1184:                     case "ENUM":
1185:                         $sqlQuery .= "$propName ENUM(";
1186: 
1187:                         $enumVals = $this->BO->getPropObject($propName)->getOptions();
1188: 
1189:                         foreach($enumVals as $val) {
1190:                             $sqlQuery .= "'".$val."',";
1191:                         }
1192: 
1193:                         $sqlQuery = rtrim($sqlQuery, ",");
1194:                         $sqlQuery .= "),";
1195:                     break;
1196:                     case "DENUM":
1197:                         $tmp = new DEnum(get_class($this->BO).'::'.$propName);
1198:                         $sqlQuery .= "$propName INT(11) ZEROFILL,";
1199:                     break;
1200:                     case "RELATION":
1201:                         $sqlQuery .= "$propName INT(11) ZEROFILL UNSIGNED,";
1202:                     break;
1203:                     default:
1204:                         $sqlQuery .= "";
1205:                     break;
1206:                 }
1207:             }
1208:         }
1209: 
1210:         if($this->BO->isTableOverloaded())
1211:             $sqlQuery .= "classname VARCHAR(100),";
1212: 
1213:         $sqlQuery .= "PRIMARY KEY (OID, version_num)) ENGINE=MyISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;";
1214: 
1215:         $this->BO->setLastQuery($sqlQuery);
1216: 
1217:         if(!$result = AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
1218:             throw new AlphaException('Failed to create the table ['.$this->BO->getTableName().'_history] for the class ['.get_class($this->BO).'], database error is ['.AlphaDAOProviderMySQL::getConnection()->error.']');
1219:             self::$logger->debug('<<makeHistoryTable');
1220:         }
1221: 
1222:         self::$logger->debug('<<makeHistoryTable');
1223:     }
1224: 
1225:     /**
1226:      * (non-PHPdoc)
1227:      * @see alpha/model/AlphaDAOProviderInterface::rebuildTable()
1228:      */
1229:     public function rebuildTable() {
1230:         self::$logger->debug('>>rebuildTable()');
1231: 
1232:         $sqlQuery = 'DROP TABLE IF EXISTS '.$this->BO->getTableName().';';
1233: 
1234:         $this->BO->setLastQuery($sqlQuery);
1235: 
1236:         if(!$result = AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
1237:             throw new AlphaException('Failed to drop the table ['.$this->BO->getTableName().'] for the class ['.get_class($this->BO).'], database error is ['.AlphaDAOProviderMySQL::getConnection()->error.']');
1238:             self::$logger->debug('<<rebuildTable');
1239:         }
1240: 
1241:         $this->BO->makeTable();
1242: 
1243:         self::$logger->debug('<<rebuildTable');
1244:     }
1245: 
1246:     /**
1247:      * (non-PHPdoc)
1248:      * @see alpha/model/AlphaDAOProviderInterface::dropTable()
1249:      */
1250:     public function dropTable($tableName=null) {
1251:         self::$logger->debug('>>dropTable()');
1252: 
1253:         if($tableName == null)
1254:             $tableName = $this->BO->getTableName();
1255: 
1256:         $sqlQuery = 'DROP TABLE IF EXISTS '.$tableName.';';
1257: 
1258:         $this->BO->setLastQuery($sqlQuery);
1259: 
1260:         if(!$result = AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
1261:             throw new AlphaException('Failed to drop the table ['.$tableName.'] for the class ['.get_class($this->BO).'], query is ['.$this->BO->getLastQuery().']');
1262:             self::$logger->debug('<<dropTable');
1263:         }
1264: 
1265:         self::$logger->debug('<<dropTable');
1266:     }
1267: 
1268:     /**
1269:      * (non-PHPdoc)
1270:      * @see alpha/model/AlphaDAOProviderInterface::addProperty()
1271:      */
1272:     public function addProperty($propName) {
1273:         self::$logger->debug('>>addProperty(propName=['.$propName.'])');
1274: 
1275:         $sqlQuery = 'ALTER TABLE '.$this->BO->getTableName().' ADD ';
1276: 
1277:         if($this->isTableOverloaded() && $propName == 'classname') {
1278:             $sqlQuery .= 'classname VARCHAR(100)';
1279:         }else{
1280:             if(!in_array($propName, $this->BO->getDefaultAttributes()) && !in_array($propName, $this->BO->getTransientAttributes())) {
1281:                 $propClass = get_class($this->BO->getPropObject($propName));
1282: 
1283:                 switch (mb_strtoupper($propClass)) {
1284:                     case 'INTEGER':
1285:                         $sqlQuery .= "$propName INT(".$this->BO->getPropObject($propName)->getSize().")";
1286:                     break;
1287:                     case 'DOUBLE':
1288:                         $sqlQuery .= "$propName DOUBLE(".$this->BO->getPropObject($propName)->getSize(true).")";
1289:                     break;
1290:                     case 'STRING':
1291:                         $sqlQuery .= "$propName VARCHAR(".$this->BO->getPropObject($propName)->getSize().")";
1292:                     break;
1293:                     case 'SEQUENCE':
1294:                         $sqlQuery .= "$propName VARCHAR(".$this->BO->getPropObject($propName)->getSize().")";
1295:                     break;
1296:                     case 'TEXT':
1297:                         $sqlQuery .= "$propName TEXT";
1298:                     break;
1299:                     case 'BOOLEAN':
1300:                         $sqlQuery .= "$propName CHAR(1) DEFAULT '0'";
1301:                     break;
1302:                     case 'DATE':
1303:                         $sqlQuery .= "$propName DATE";
1304:                     break;
1305:                     case 'TIMESTAMP':
1306:                         $sqlQuery .= "$propName DATETIME";
1307:                     break;
1308:                     case 'ENUM':
1309:                         $sqlQuery .= "$propName ENUM(";
1310:                         $enumVals = $this->BO->getPropObject($propName)->getOptions();
1311:                         foreach($enumVals as $val) {
1312:                             $sqlQuery .= "'".$val."',";
1313:                         }
1314:                         $sqlQuery = rtrim($sqlQuery, ",");
1315:                         $sqlQuery .= ')';
1316:                     break;
1317:                     case 'DENUM':
1318:                         $tmp = new DEnum(get_class($this->BO).'::'.$propName);
1319:                         $tmp->save();
1320:                         $sqlQuery .= "$propName INT(11) ZEROFILL";
1321:                     break;
1322:                     case 'RELATION':
1323:                         $sqlQuery .= "$propName INT(11) ZEROFILL UNSIGNED";
1324:                     break;
1325:                     default:
1326:                         $sqlQuery .= '';
1327:                     break;
1328:                 }
1329:             }
1330:         }
1331: 
1332:         $this->BO->setLastQuery($sqlQuery);
1333: 
1334:         if(!$result = self::getConnection()->query($sqlQuery)) {
1335:             throw new AlphaException('Failed to add the new attribute ['.$propName.'] to the table ['.$this->BO->getTableName().'], query is ['.$this->BO->getLastQuery().']');
1336:             self::$logger->debug('<<addProperty');
1337:         }else{
1338:             self::$logger->info('Successfully added the ['.$propName.'] column onto the ['.$this->BO->getTableName().'] table for the class ['.get_class($this->BO).']');
1339:         }
1340: 
1341:         self::$logger->debug('<<addProperty');
1342:     }
1343: 
1344:     /**
1345:      * (non-PHPdoc)
1346:      * @see alpha/model/AlphaDAOProviderInterface::getMAX()
1347:      */
1348:     public function getMAX() {
1349:         self::$logger->debug('>>getMAX()');
1350: 
1351:         $sqlQuery = 'SELECT MAX(OID) AS max_OID FROM '.$this->BO->getTableName();
1352: 
1353:         $this->BO->setLastQuery($sqlQuery);
1354: 
1355:         try {
1356:             $result = $this->BO->query($sqlQuery);
1357: 
1358:             $row = $result[0];
1359: 
1360:             if (isset($row['max_OID'])) {
1361:                 self::$logger->debug('<<getMAX ['.$row['max_OID'].']');
1362:                 return $row['max_OID'];
1363:             }else{
1364:                 throw new AlphaException('Failed to get the MAX ID for the class ['.get_class($this->BO).'] from the table ['.$this->BO->getTableName().'], query is ['.$this->BO->getLastQuery().']');
1365:             }
1366:         }catch (Exception $e) {
1367:             throw new AlphaException($e->getMessage());
1368:             self::$logger->debug('<<getMAX [0]');
1369:             return 0;
1370:         }
1371:     }
1372: 
1373:     /**
1374:      * (non-PHPdoc)
1375:      * @see alpha/model/AlphaDAOProviderInterface::getCount()
1376:      */
1377:     public function getCount($attributes=array(), $values=array()) {
1378:         self::$logger->debug('>>getCount(attributes=['.var_export($attributes, true).'], values=['.var_export($values, true).'])');
1379: 
1380:         if($this->BO->isTableOverloaded())
1381:             $whereClause = ' WHERE classname = \''.get_class($this->BO).'\' AND';
1382:         else
1383:             $whereClause = ' WHERE';
1384: 
1385:         $count = count($attributes);
1386: 
1387:         for($i = 0; $i < $count; $i++) {
1388:             $whereClause .= ' '.$attributes[$i].' = \''.$values[$i].'\' AND';
1389:             self::$logger->debug($whereClause);
1390:         }
1391:         // remove the last " AND"
1392:         $whereClause = mb_substr($whereClause, 0, -4);
1393: 
1394:         if($whereClause != ' WHERE')
1395:             $sqlQuery = 'SELECT COUNT(OID) AS class_count FROM '.$this->BO->getTableName().$whereClause;
1396:         else
1397:             $sqlQuery = 'SELECT COUNT(OID) AS class_count FROM '.$this->BO->getTableName();
1398: 
1399:         $this->BO->setLastQuery($sqlQuery);
1400: 
1401:         $result = self::getConnection()->query($sqlQuery);
1402: 
1403:         if ($result) {
1404:             $row = $result->fetch_array(MYSQLI_ASSOC);
1405: 
1406:             self::$logger->debug('<<getCount ['.$row['class_count'].']');
1407:             return $row['class_count'];
1408:         }else{
1409:             throw new AlphaException('Failed to get the count for the class ['.get_class($this->BO).'] from the table ['.$this->BO->getTableName().'], query is ['.$this->BO->getLastQuery().']');
1410:             self::$logger->debug('<<getCount [0]');
1411:             return 0;
1412:         }
1413:     }
1414: 
1415:     /**
1416:          * (non-PHPdoc)
1417:          * @see alpha/model/AlphaDAOProviderInterface::getHistoryCount()
1418:          */
1419:     public function getHistoryCount() {
1420:         self::$logger->debug('>>getHistoryCount()');
1421: 
1422:         if(!$this->BO->getMaintainHistory())
1423:             throw new AlphaException('getHistoryCount method called on a DAO where no history is maintained!');
1424: 
1425:         $sqlQuery = 'SELECT COUNT(OID) AS object_count FROM '.$this->BO->getTableName().'_history WHERE OID='.$this->BO->getOID();
1426: 
1427:                 $this->BO->setLastQuery($sqlQuery);
1428: 
1429:                 $result = self::getConnection()->query($sqlQuery);
1430: 
1431:                 if ($result) {
1432:                     $row = $result->fetch_array(MYSQLI_ASSOC);
1433: 
1434:                     self::$logger->debug('<<getHistoryCount ['.$row['object_count'].']');
1435:                     return $row['object_count'];
1436:                 }else{
1437:                     throw new AlphaException('Failed to get the history count for the business object ['.$this->BO->getOID().'] from the table ['.$this->BO->getTableName().'_history], query is ['.$this->BO->getLastQuery().']');
1438:                     self::$logger->debug('<<getHistoryCount [0]');
1439:                     return 0;
1440:                 }
1441:     }
1442: 
1443:     /**
1444:      * (non-PHPdoc)
1445:      * @see alpha/model/AlphaDAOProviderInterface::setEnumOptions()
1446:      * @since 1.1
1447:      */
1448:     public function setEnumOptions() {
1449:         self::$logger->debug('>>setEnumOptions()');
1450: 
1451:         // get the class attributes
1452:         $reflection = new ReflectionClass(get_class($this->BO));
1453:         $properties = $reflection->getProperties();
1454: 
1455:         // flag for any database errors
1456:         $dbError = false;
1457: 
1458:         foreach($properties as $propObj) {
1459:             $propName = $propObj->name;
1460:             if(!in_array($propName, $this->BO->getDefaultAttributes()) && !in_array($propName, $this->BO->getTransientAttributes())) {
1461:                 $propClass = get_class($this->BO->getPropObject($propName));
1462:                 if ($propClass == 'Enum') {
1463:                     $sqlQuery = "SHOW COLUMNS FROM ".$this->BO->getTableName()." LIKE '$propName'";
1464: 
1465:                     $this->BO->setLastQuery($sqlQuery);
1466: 
1467:                     $result = AlphaDAOProviderMySQL::getConnection()->query($sqlQuery);
1468: 
1469:                     if ($result) {
1470:                         $row = $result->fetch_array(MYSQLI_NUM);
1471:                         $options = explode("','",preg_replace("/(enum|set)\('(.+?)'\)/","\\2",$row[1]));
1472: 
1473:                         $this->BO->getPropObject($propName)->setOptions($options);
1474:                     }else{
1475:                         $dbError = true;
1476:                         break;
1477:                     }
1478:                 }
1479:             }
1480:         }
1481: 
1482:         if (!$dbError) {
1483:             if(method_exists($this, 'after_setEnumOptions_callback'))
1484:                 $this->after_setEnumOptions_callback();
1485:         }else{
1486:             throw new AlphaException('Failed to load enum options correctly for object instance of class ['.get_class($this).']');
1487:         }
1488:         self::$logger->debug('<<setEnumOptions');
1489:     }
1490: 
1491:     /**
1492:      * (non-PHPdoc)
1493:      * @see alpha/model/AlphaDAOProviderInterface::checkTableExists()
1494:      */
1495:     public function checkTableExists($checkHistoryTable = false) {
1496:         self::$logger->debug('>>checkTableExists(checkHistoryTable=['.$checkHistoryTable.'])');
1497: 
1498:         global $config;
1499: 
1500:         $tableExists = false;
1501: 
1502:         $sqlQuery = 'SHOW TABLES;';
1503:         $this->BO->setLastQuery($sqlQuery);
1504: 
1505:         $result = self::getConnection()->query($sqlQuery);
1506: 
1507:         if ($result) {
1508:             $tableName = ($checkHistoryTable ? $this->BO->getTableName().'_history' : $this->BO->getTableName());
1509: 
1510:             while ($row = $result->fetch_array(MYSQLI_NUM)) {
1511:                 if (strtolower($row[0]) == mb_strtolower($tableName))
1512:                     $tableExists = true;
1513:             }
1514: 
1515:             self::$logger->debug('<<checkTableExists ['.$tableExists.']');
1516:             return $tableExists;
1517:         }else{
1518:             throw new AlphaException('Failed to access the system database correctly, error is ['.self::getConnection()->error.']');
1519:             self::$logger->debug('<<checkTableExists [false]');
1520:             return false;
1521:         }
1522:     }
1523: 
1524:     /**
1525:      * (non-PHPdoc)
1526:      * @see alpha/model/AlphaDAOProviderInterface::checkBOTableExists()
1527:      */
1528:     public static function checkBOTableExists($BOClassName, $checkHistoryTable = false) {
1529:         if(self::$logger == null)
1530:             self::$logger = new Logger('AlphaDAOProvidermySQL');
1531:         self::$logger->debug('>>checkBOTableExists(BOClassName=['.$BOClassName.'], checkHistoryTable=['.$checkHistoryTable.'])');
1532: 
1533:         eval('$tableName = '.$BOClassName.'::TABLE_NAME;');
1534: 
1535:         if(empty($tableName))
1536:             $tableName = mb_substr($BOClassName, 0, mb_strpos($BOClassName, '_'));
1537: 
1538:         if($checkHistoryTable)
1539:             $tableName .= '_history';
1540: 
1541:         $tableExists = false;
1542: 
1543:         $sqlQuery = 'SHOW TABLES;';
1544: 
1545:         $result = self::getConnection()->query($sqlQuery);
1546: 
1547:         while ($row = $result->fetch_array(MYSQLI_NUM)) {
1548:                 if ($row[0] == $tableName)
1549:                     $tableExists = true;
1550:         }
1551: 
1552:         if ($result) {
1553:             self::$logger->debug('<<checkBOTableExists ['.($tableExists ? 'true' : 'false').']');
1554:             return $tableExists;
1555:         }else{
1556:             throw new AlphaException('Failed to access the system database correctly, error is ['.self::getConnection()->error.']');
1557:             self::$logger->debug('<<checkBOTableExists [false]');
1558:             return false;
1559:         }
1560:     }
1561: 
1562:     /**
1563:      * (non-PHPdoc)
1564:      * @see alpha/model/AlphaDAOProviderInterface::checkTableNeedsUpdate()
1565:      */
1566:     public function checkTableNeedsUpdate() {
1567:         self::$logger->debug('>>checkTableNeedsUpdate()');
1568: 
1569:         $updateRequired = false;
1570: 
1571:         $matchCount = 0;
1572: 
1573:         $query = 'SHOW COLUMNS FROM '.$this->BO->getTableName();
1574:         $result = self::getConnection()->query($query);
1575:         $this->BO->setLastQuery($query);
1576: 
1577:         // get the class attributes
1578:         $reflection = new ReflectionClass(get_class($this->BO));
1579:         $properties = $reflection->getProperties();
1580: 
1581:         foreach($properties as $propObj) {
1582:             $propName = $propObj->name;
1583:             if (!in_array($propName, $this->BO->getTransientAttributes())) {
1584: 
1585:                 $foundMatch = false;
1586: 
1587:                 while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
1588:                     if ($propName == $row['Field']) {
1589:                         $foundMatch = true;
1590:                         break;
1591:                     }
1592:                 }
1593: 
1594:                 if(!$foundMatch)
1595:                     $matchCount--;
1596: 
1597:                 $result->data_seek(0);
1598:             }
1599:         }
1600: 
1601:         // check for the "classname" field in overloaded tables
1602:         if($this->BO->isTableOverloaded()) {
1603:             $foundMatch = false;
1604: 
1605:             while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
1606:                 if ('classname' == $row['Field']) {
1607:                     $foundMatch = true;
1608:                     break;
1609:                 }
1610:             }
1611:             if(!$foundMatch)
1612:                 $matchCount--;
1613:         }
1614: 
1615:         if ($matchCount != 0)
1616:             $updateRequired = true;
1617: 
1618:         if ($result) {
1619:             // check the table indexes
1620:             try {
1621:                 $this->checkIndexes();
1622:             }catch (AlphaException $ae) {
1623:                 self::$logger->warn("Error while checking database indexes:\n\n".$ae->getMessage());
1624:             }
1625: 
1626:             self::$logger->debug('<<checkTableNeedsUpdate ['.$updateRequired.']');
1627:             return $updateRequired;
1628:         }else{
1629:             throw new AlphaException('Failed to access the system database correctly, error is ['.self::getConnection()->error.']');
1630:             self::$logger->debug('<<checkTableNeedsUpdate [false]');
1631:             return false;
1632:         }
1633:     }
1634: 
1635:     /**
1636:      * (non-PHPdoc)
1637:      * @see alpha/model/AlphaDAOProviderInterface::findMissingFields()
1638:      */
1639:     public function findMissingFields() {
1640:         self::$logger->debug('>>findMissingFields()');
1641: 
1642:         $missingFields = array();
1643:         $matchCount = 0;
1644: 
1645:         $sqlQuery = 'SHOW COLUMNS FROM '.$this->BO->getTableName();
1646: 
1647:         $result = self::getConnection()->query($sqlQuery);
1648: 
1649:         $this->BO->setLastQuery($sqlQuery);
1650: 
1651:         // get the class attributes
1652:         $reflection = new ReflectionClass(get_class($this->BO));
1653:         $properties = $reflection->getProperties();
1654: 
1655:         foreach($properties as $propObj) {
1656:             $propName = $propObj->name;
1657:             if (!in_array($propName, $this->BO->getTransientAttributes())) {
1658:                 while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
1659:                     if ($propName == $row['Field']) {
1660:                         $matchCount++;
1661:                         break;
1662:                     }
1663:                 }
1664:                 $result->data_seek(0);
1665:             }else{
1666:                 $matchCount++;
1667:             }
1668: 
1669:             if($matchCount==0) {
1670:                 array_push($missingFields, $propName);
1671:             }else{
1672:                 $matchCount = 0;
1673:             }
1674:         }
1675: 
1676:         // check for the "classname" field in overloaded tables
1677:         if($this->BO->isTableOverloaded()) {
1678:             $foundMatch = false;
1679: 
1680:             while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
1681:                 if ('classname' == $row['Field']) {
1682:                     $foundMatch = true;
1683:                     break;
1684:                 }
1685:             }
1686:             if(!$foundMatch)
1687:                 array_push($missingFields, 'classname');
1688:         }
1689: 
1690:         if (!$result) {
1691:             throw new AlphaException('Failed to access the system database correctly, error is ['.self::getConnection()->error.']');
1692:         }
1693: 
1694:         self::$logger->debug('<<findMissingFields ['.var_export($missingFields, true).']');
1695:         return $missingFields;
1696:     }
1697: 
1698:     /**
1699:      * (non-PHPdoc)
1700:      * @see alpha/model/AlphaDAOProviderInterface::getIndexes()
1701:      */
1702:     public function getIndexes() {
1703:         self::$logger->debug('>>getIndexes()');
1704: 
1705:         $query = 'SHOW INDEX FROM '.$this->BO->getTableName();
1706: 
1707:         $result = self::getConnection()->query($query);
1708: 
1709:         $this->BO->setLastQuery($query);
1710: 
1711:         $indexNames = array();
1712: 
1713:         if (!$result) {
1714:             throw new AlphaException('Failed to access the system database correctly, error is ['.self::getConnection()->error.']');
1715:         }else{
1716:             while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
1717:                 array_push($indexNames, $row['Key_name']);
1718:             }
1719:         }
1720: 
1721:         self::$logger->debug('<<getIndexes');
1722:         return $indexNames;
1723:     }
1724: 
1725:     /**
1726:      * Checks to see if all of the indexes are in place for the BO's table, creates those that are missing.
1727:      *
1728:      * @since 1.1
1729:      */
1730:     private function checkIndexes() {
1731:         self::$logger->debug('>>checkIndexes()');
1732: 
1733:         $indexNames = $this->getIndexes();
1734: 
1735:         // process unique keys
1736:         foreach($this->BO->getUniqueAttributes() as $prop) {
1737:             // check for composite indexes
1738:             if(mb_strpos($prop, '+')) {
1739:                 $attributes = explode('+', $prop);
1740: 
1741:                 $index_exists = false;
1742:                 foreach ($indexNames as $index) {
1743:                     if ($attributes[0].'_'.$attributes[1].'_unq_idx' == $index) {
1744:                         $index_exists = true;
1745:                     }
1746:                     if(count($attributes) == 3) {
1747:                         if ($attributes[0].'_'.$attributes[1].'_'.$attributes[2].'_unq_idx' == $index) {
1748:                             $index_exists = true;
1749:                         }
1750:                     }
1751:                 }
1752: 
1753:                 if(!$index_exists) {
1754:                     if(count($attributes) == 3)
1755:                         $this->BO->createUniqueIndex($attributes[0], $attributes[1], $attributes[2]);
1756:                     else
1757:                         $this->BO->createUniqueIndex($attributes[0], $attributes[1]);
1758:                 }
1759:             }else{
1760:                 $index_exists = false;
1761:                 foreach ($indexNames as $index) {
1762:                     if ($prop.'_unq_idx' == $index) {
1763:                         $index_exists = true;
1764:                     }
1765:                 }
1766: 
1767:                 if(!$index_exists)
1768:                     $this->createUniqueIndex($prop);
1769:             }
1770:         }
1771: 
1772:         // process foreign-key indexes
1773:         // get the class attributes
1774:         $reflection = new ReflectionClass(get_class($this->BO));
1775:         $properties = $reflection->getProperties();
1776: 
1777:         foreach($properties as $propObj) {
1778:             $propName = $propObj->name;
1779:             $prop = $this->BO->getPropObject($propName);
1780:             if($prop instanceof Relation) {
1781: 
1782:                 if($prop->getRelationType() == 'MANY-TO-ONE') {
1783:                     $indexExists = false;
1784:                     foreach ($indexNames as $index) {
1785:                         if ($propName.'_fk_idx' == $index) {
1786:                             $indexExists = true;
1787:                         }
1788:                     }
1789: 
1790:                     if(!$indexExists) {
1791:                         $this->createForeignIndex($propName, $prop->getRelatedClass(), $prop->getRelatedClassField());
1792:                     }
1793:                 }
1794: 
1795:                 if($prop->getRelationType() == 'MANY-TO-MANY') {
1796:                     $lookup = $prop->getLookup();
1797: 
1798:                     if($lookup != null) {
1799:                         try {
1800:                             $lookupIndexNames = $lookup->getIndexes();
1801: 
1802:                             // handle index check/creation on left side of Relation
1803:                             $indexExists = false;
1804:                             foreach ($lookupIndexNames as $index) {
1805:                                 if ('leftID_fk_idx' == $index) {
1806:                                     $indexExists = true;
1807:                                 }
1808:                             }
1809: 
1810:                             if(!$indexExists) {
1811:                                 $lookup->createForeignIndex('leftID', $prop->getRelatedClass('left'), 'OID');
1812:                             }
1813: 
1814:                             // handle index check/creation on right side of Relation
1815:                             $indexExists = false;
1816:                             foreach ($lookupIndexNames as $index) {
1817:                                 if ('rightID_fk_idx' == $index) {
1818:                                     $indexExists = true;
1819:                                 }
1820:                             }
1821: 
1822:                             if(!$indexExists) {
1823:                                 $lookup->createForeignIndex('rightID', $prop->getRelatedClass('right'), 'OID');
1824:                             }
1825:                         }catch(AlphaException $e) {
1826:                             self::$logger->error($e->getMessage());
1827:                         }
1828:                     }
1829:                 }
1830: 
1831:             }
1832:         }
1833: 
1834:         self::$logger->debug('<<checkIndexes');
1835:     }
1836: 
1837:     /**
1838:      * (non-PHPdoc)
1839:      * @see alpha/model/AlphaDAOProviderInterface::createForeignIndex()
1840:      */
1841:     public function createForeignIndex($attributeName, $relatedClass, $relatedClassAttribute) {
1842:         self::$logger->debug('>>createForeignIndex(attributeName=['.$attributeName.'], relatedClass=['.$relatedClass.'], relatedClassAttribute=['.$relatedClassAttribute.']');
1843: 
1844:         AlphaDAO::loadClassDef($relatedClass);
1845:         $relatedBO = new $relatedClass;
1846:         $tableName = $relatedBO->getTableName();
1847: 
1848:         $result = false;
1849: 
1850:         if(self::checkBOTableExists(ucfirst($tableName).'Object')) {
1851:             $sqlQuery = '';
1852: 
1853:             if($attributeName == 'leftID')
1854:                 $sqlQuery = 'ALTER TABLE '.$this->BO->getTableName().' ADD INDEX leftID_fk_idx (leftID);';
1855:             if($attributeName == 'rightID')
1856:                 $sqlQuery = 'ALTER TABLE '.$this->BO->getTableName().' ADD INDEX rightID_fk_idx (rightID);';
1857: 
1858:             if(!empty($sqlQuery)) {
1859:                 $this->BO->setLastQuery($sqlQuery);
1860: 
1861:                 $result = self::getConnection()->query($sqlQuery);
1862: 
1863:                 if (!$result) {
1864:                     throw new FailedIndexCreateException('Failed to create an index on ['.$this->BO->getTableName().'], error is ['.self::getConnection()->error.'], query ['.$this->BO->getLastQuery().']');
1865:                 }
1866:             }
1867: 
1868:             $sqlQuery = 'ALTER TABLE '.$this->BO->getTableName().' ADD FOREIGN KEY '.$attributeName.'_fk_idx ('.$attributeName.') REFERENCES '.$tableName.' ('.$relatedClassAttribute.') ON DELETE SET NULL;';
1869: 
1870:             $this->BO->setLastQuery($sqlQuery);
1871:             $result = self::getConnection()->query($sqlQuery);
1872:         }
1873: 
1874:         if ($result) {
1875:             self::$logger->debug('Successfully created the foreign key index ['.$attributeName.'_fk_idx]');
1876:         }else{
1877:             throw new FailedIndexCreateException('Failed to create the index ['.$attributeName.'_fk_idx] on ['.$this->BO->getTableName().'], error is ['.self::getConnection()->error.'], query ['.$this->BO->getLastQuery().']');
1878:         }
1879: 
1880:         self::$logger->debug('<<createForeignIndex');
1881:     }
1882: 
1883:     /**
1884:      * (non-PHPdoc)
1885:      * @see alpha/model/AlphaDAOProviderInterface::createUniqueIndex()
1886:      */
1887:     public function createUniqueIndex($attribute1Name, $attribute2Name = '', $attribute3Name = '') {
1888:         self::$logger->debug('>>createUniqueIndex(attribute1Name=['.$attribute1Name.'], attribute2Name=['.$attribute2Name.'], attribute3Name=['.$attribute3Name.'])');
1889: 
1890:         if($attribute2Name != '' && $attribute3Name != '')
1891:             $sqlQuery = 'CREATE UNIQUE INDEX '.$attribute1Name.'_'.$attribute2Name.'_'.$attribute3Name.'_unq_idx ON '.$this->BO->getTableName().' ('.$attribute1Name.','.$attribute2Name.','.$attribute3Name.');';
1892: 
1893:         if($attribute2Name != '' && $attribute3Name == '')
1894:             $sqlQuery = 'CREATE UNIQUE INDEX '.$attribute1Name.'_'.$attribute2Name.'_unq_idx ON '.$this->BO->getTableName().' ('.$attribute1Name.','.$attribute2Name.');';
1895: 
1896:         if($attribute2Name == '' && $attribute3Name == '')
1897:             $sqlQuery = 'CREATE UNIQUE INDEX '.$attribute1Name.'_unq_idx ON '.$this->BO->getTableName().' ('.$attribute1Name.');';
1898: 
1899:         $this->BO->setLastQuery($sqlQuery);
1900: 
1901:         $result = self::getConnection()->query($sqlQuery);
1902: 
1903:         if ($result) {
1904:             self::$logger->debug('Successfully created the unique index on ['.$this->BO->getTableName().']');
1905:         }else{
1906:             throw new FailedIndexCreateException('Failed to create the unique index on ['.$this->BO->getTableName().'], error is ['.self::getConnection()->error.']');
1907:         }
1908: 
1909:         self::$logger->debug('<<createUniqueIndex');
1910:     }
1911: 
1912:     /**
1913:      * (non-PHPdoc)
1914:      * @see alpha/model/AlphaDAOProviderInterface::reload()
1915:      */
1916:     public function reload() {
1917:         self::$logger->debug('>>reload()');
1918: 
1919:         if(!$this->isTransient()) {
1920:             $this->load($this->getOID());
1921:         }else{
1922:             throw new AlphaException('Cannot reload transient object from database!');
1923:         }
1924:         self::$logger->debug('<<reload');
1925:     }
1926: 
1927:     /**
1928:      * (non-PHPdoc)
1929:      * @see alpha/model/AlphaDAOProviderInterface::checkRecordExists()
1930:      */
1931:     public function checkRecordExists($OID) {
1932:         self::$logger->debug('>>checkRecordExists(OID=['.$OID.'])');
1933: 
1934:         $sqlQuery = 'SELECT OID FROM '.$this->BO->getTableName().' WHERE OID = ?;';
1935: 
1936:         $this->BO->setLastQuery($sqlQuery);
1937: 
1938:         $stmt = self::getConnection()->stmt_init();
1939: 
1940:         if($stmt->prepare($sqlQuery)) {
1941:             $stmt->bind_param('i', $OID);
1942: 
1943:             $stmt->execute();
1944: 
1945:             $result = $this->bindResult($stmt);
1946: 
1947:             $stmt->close();
1948: 
1949:             if ($result) {
1950:                 if(count($result) > 0) {
1951:                     self::$logger->debug('<<checkRecordExists [true]');
1952:                     return true;
1953:                 }else{
1954:                     self::$logger->debug('<<checkRecordExists [false]');
1955:                     return false;
1956:                 }
1957:             }else{
1958:                 throw new AlphaException('Failed to check for the record ['.$OID.'] on the class ['.get_class($this->BO).'] from the table ['.$this->BO->getTableName().'], query is ['.$this->BO->getLastQuery().']');
1959:                 self::$logger->debug('<<checkRecordExists [false]');
1960:                 return false;
1961:             }
1962:         }else{
1963:             throw new AlphaException('Failed to check for the record ['.$OID.'] on the class ['.get_class($this->BO).'] from the table ['.$this->BO->getTableName().'], query is ['.$this->BO->getLastQuery().']');
1964:             self::$logger->debug('<<checkRecordExists [false]');
1965:             return false;
1966:         }
1967:     }
1968: 
1969:     /**
1970:      * (non-PHPdoc)
1971:      * @see alpha/model/AlphaDAOProviderInterface::isTableOverloaded()
1972:      */
1973:     public function isTableOverloaded() {
1974:         self::$logger->debug('>>isTableOverloaded()');
1975: 
1976:         $classname = get_class($this->BO);
1977:         $tablename = ucfirst($this->BO->getTableName()).'Object';
1978: 
1979:         // use reflection to check to see if we are dealing with a persistent type (e.g. DEnum) which are never overloaded
1980:         $reflection = new ReflectionClass($classname);
1981:         $implementedInterfaces = $reflection->getInterfaces();
1982: 
1983:         foreach ($implementedInterfaces as $interface) {
1984:             if ($interface->name == 'AlphaTypeInterface') {
1985:                 self::$logger->debug('<<isTableOverloaded [false]');
1986:                 return false;
1987:             }
1988:         }
1989: 
1990:         if($classname != $tablename) {
1991:             // loop over all BOs to see if there is one using the same table as this BO
1992: 
1993:             $BOclasses = AlphaDAO::getBOClassNames();
1994: 
1995:             foreach($BOclasses as $BOclassName) {
1996:                 if($tablename == $BOclassName) {
1997:                     self::$logger->debug('<<isTableOverloaded [true]');
1998:                     return true;
1999:                 }
2000:             }
2001:             throw new BadBOTableNameException('The table name ['.$tablename.'] for the class ['.$classname.'] is invalid as it does not match a BO definition in the system!');
2002:             self::$logger->debug('<<isTableOverloaded [false]');
2003:             return false;
2004:         }else{
2005:             // check to see if there is already a "classname" column in the database for this BO
2006: 
2007:             $query = 'SHOW COLUMNS FROM '.$this->BO->getTableName();
2008: 
2009:             $result = self::getConnection()->query($query);
2010: 
2011:             if($result) {
2012:                 while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
2013:                     if ('classname' == $row['Field']) {
2014:                         self::$logger->debug('<<isTableOverloaded [true]');
2015:                         return true;
2016:                     }
2017:                 }
2018:             }else{
2019:                 self::$logger->warn('Error during show columns ['.self::getConnection()->error.']');
2020:             }
2021: 
2022:             self::$logger->debug('<<isTableOverloaded [false]');
2023:             return false;
2024:         }
2025:     }
2026: 
2027:     /**
2028:      * (non-PHPdoc)
2029:      * @see alpha/model/AlphaDAOProviderInterface::begin()
2030:      */
2031:     public static function begin() {
2032:         if(self::$logger == null)
2033:             self::$logger = new Logger('AlphaDAOProviderMySQL');
2034:         self::$logger->debug('>>begin()');
2035: 
2036:         if (!self::getConnection()->autocommit(false))
2037:             throw new AlphaException('Error beginning a new transaction, error is ['.self::getConnection()->error.']');
2038: 
2039:         self::$logger->debug('<<begin');
2040:     }
2041: 
2042:     /**
2043:      * (non-PHPdoc)
2044:      * @see alpha/model/AlphaDAOProviderInterface::commit()
2045:      */
2046:     public static function commit() {
2047:         if(self::$logger == null)
2048:             self::$logger = new Logger('AlphaDAOProviderMySQL');
2049:         self::$logger->debug('>>commit()');
2050: 
2051:         if (!self::getConnection()->commit())
2052:             throw new FailedSaveException('Error commiting a transaction, error is ['.self::getConnection()->error.']');
2053: 
2054:         self::$logger->debug('<<commit');
2055:     }
2056: 
2057:     /**
2058:      * (non-PHPdoc)
2059:      * @see alpha/model/AlphaDAOProviderInterface::rollback()
2060:      */
2061:     public static function rollback() {
2062:         if(self::$logger == null)
2063:             self::$logger = new Logger('AlphaDAOProviderMySQL');
2064:         self::$logger->debug('>>rollback()');
2065: 
2066:         if (!self::getConnection()->rollback())
2067:             throw new AlphaException('Error rolling back a transaction, error is ['.self::getConnection()->error.']');
2068: 
2069:         self::$logger->debug('<<rollback');
2070:     }
2071: 
2072:     /**
2073:      * (non-PHPdoc)
2074:      * @see alpha/model/AlphaDAOProviderInterface::setBO()
2075:      */
2076:     public function setBO($BO) {
2077:         $this->BO = $BO;
2078:     }
2079: 
2080:     /**
2081:      * Dynamically binds all of the attributes for the current BO to the supplied prepared statement
2082:      * parameters.  If arrays of attribute names and values are provided, only those will be bound to
2083:      * the supplied statement.
2084:      *
2085:      * @param mysqli_stmt $stmt The SQL statement to bind to.
2086:      * @param array Optional array of BO attributes.
2087:      * @param array Optional array of BO values.
2088:      * @return mysqli_stmt
2089:      * @since 1.1
2090:      */
2091:     private function bindParams($stmt, $attributes=array(), $values=array()) {
2092:         self::$logger->debug('>>bindParams(stmt=['.var_export($stmt, true).'])');
2093: 
2094:         $bindingsTypes = '';
2095:         $params = array();
2096: 
2097:         // here we are only binding the supplied attributes
2098:         if(count($attributes) > 0 && count($attributes) == count($values)) {
2099: 
2100:             $count = count($values);
2101: 
2102:             for($i = 0; $i < $count; $i++) {
2103:                 if (AlphaValidator::isInteger($values[$i]))
2104:                     $bindingsTypes .= 'i';
2105:                 else
2106:                     $bindingsTypes .= 's';
2107:                 array_push($params, $values[$i]);
2108:             }
2109: 
2110:             if($this->BO->isTableOverloaded()) {
2111:                 if(isset($this->classname)) {
2112:                     $bindingsTypes .= 's';
2113:                     array_push($params, $this->classname);
2114:                 }else{
2115:                     $bindingsTypes .= 's';
2116:                     array_push($params, get_class($this->BO));
2117:                 }
2118:             }
2119:         }else{ // bind all attributes on the business object
2120: 
2121:             // get the class attributes
2122:             $reflection = new ReflectionClass(get_class($this->BO));
2123:             $properties = $reflection->getProperties();
2124: 
2125:             foreach($properties as $propObj) {
2126:                 $propName = $propObj->name;
2127:                 if (!in_array($propName, $this->BO->getTransientAttributes())) {
2128:                     // Skip the OID, database auto number takes care of this.
2129:                     if($propName != 'OID' && $propName != 'version_num') {
2130:                         if($this->BO->getPropObject($propName) instanceof Integer)
2131:                             $bindingsTypes .= 'i';
2132:                         else
2133:                             $bindingsTypes .= 's';
2134:                         array_push($params, $this->BO->get($propName));
2135:                     }
2136: 
2137:                     if($propName == 'version_num') {
2138:                         $temp = $this->BO->getVersionNumber()->getValue();
2139:                         $this->BO->set('version_num', $temp+1);
2140:                         $bindingsTypes .= 'i';
2141:                         array_push($params, $this->BO->getVersionNumber()->getValue());
2142:                     }
2143:                 }
2144:             }
2145: 
2146:             if($this->BO->isTableOverloaded()) {
2147:                 if(isset($this->classname)) {
2148:                     $bindingsTypes .= 's';
2149:                     array_push($params, $this->classname);
2150:                 }else{
2151:                     $bindingsTypes .= 's';
2152:                     array_push($params, get_class($this->BO));
2153:                 }
2154:             }
2155: 
2156:             // the OID may be on the WHERE clause for UPDATEs and DELETEs
2157:             if(!$this->BO->isTransient()) {
2158:                 $bindingsTypes .= 'i';
2159:                 array_push($params, $this->BO->getOID());
2160:             }
2161:         }
2162: 
2163:         self::$logger->debug('bindingsTypes=['.$bindingsTypes.'], count: ['.mb_strlen($bindingsTypes).']');
2164:         self::$logger->debug('params ['.var_export($params, true).']');
2165: 
2166:         if ($params != null) {
2167:             $bind_names[] = $bindingsTypes;
2168: 
2169:             $count = count($params);
2170: 
2171:             for ($i = 0; $i < $count; $i++) {
2172:                 $bind_name = 'bind'.$i;
2173:                 $$bind_name = $params[$i];
2174:                 $bind_names[] = &$$bind_name;
2175:             }
2176: 
2177:             call_user_func_array(array($stmt,'bind_param'), $bind_names);
2178:         }
2179: 
2180:         self::$logger->debug('<<bindParams ['.var_export($stmt, true).']');
2181:         return $stmt;
2182:     }
2183: 
2184:     /**
2185:      * Dynamically binds the result of the supplied prepared statement to a 2d array, where each element in the array is another array
2186:      * representing a database row.
2187:      *
2188:      * @param mysqli_stmt $stmt
2189:      * @return array A 2D array containing the query result.
2190:      * @since 1.1
2191:      */
2192:     private function bindResult($stmt) {
2193:         $result = array();
2194: 
2195:         $metadata = $stmt->result_metadata();
2196:         $fields = $metadata->fetch_fields();
2197: 
2198:         while(true) {
2199:             $pointers = array();
2200:             $row = array();
2201: 
2202:             $pointers[] = $stmt;
2203:             foreach ($fields as $field) {
2204:                 $fieldname = $field->name;
2205:                 $pointers[] = &$row[$fieldname];
2206:             }
2207: 
2208:             call_user_func_array('mysqli_stmt_bind_result', $pointers);
2209: 
2210:             if (!$stmt->fetch())
2211:                 break;
2212: 
2213:             $result[] = $row;
2214:         }
2215: 
2216:         $metadata->free();
2217: 
2218:         return $result;
2219:     }
2220: 
2221:     /**
2222:      * Parses a MySQL error for the value that violated a unique constraint.
2223:      *
2224:      * @param string $error The MySQL error string.
2225:      * @since 1.1
2226:      */
2227:     private function findOffendingValue($error) {
2228:         self::$logger->debug('>>findOffendingValue(error=['.$error.'])');
2229: 
2230:         $singleQuote1 = mb_strpos($error,"'");
2231:         $singleQuote2 = mb_strrpos($error,"'");
2232: 
2233:         $value = mb_substr($error, $singleQuote1, ($singleQuote2-$singleQuote1)+1);
2234:         self::$logger->debug('<<findOffendingValue ['.$value.'])');
2235:         return $value;
2236:     }
2237: }
2238: 
2239: ?>
Alpha Framework 1.2.4 API Documentation API documentation generated by ApiGen 2.8.0