[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/db/ -> ORMTable.php (source)

   1  <?php
   2  /**
   3   * Abstract base class for representing a single database table.
   4   * Documentation inline and at https://www.mediawiki.org/wiki/Manual:ORMTable
   5   *
   6   * This program is free software; you can redistribute it and/or modify
   7   * it under the terms of the GNU General Public License as published by
   8   * the Free Software Foundation; either version 2 of the License, or
   9   * (at your option) any later version.
  10   *
  11   * This program is distributed in the hope that it will be useful,
  12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14   * GNU General Public License for more details.
  15   *
  16   * You should have received a copy of the GNU General Public License along
  17   * with this program; if not, write to the Free Software Foundation, Inc.,
  18   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19   * http://www.gnu.org/copyleft/gpl.html
  20   *
  21   * @since 1.20
  22   * Non-abstract since 1.21
  23   *
  24   * @file ORMTable.php
  25   * @ingroup ORM
  26   *
  27   * @license GNU GPL v2 or later
  28   * @author Jeroen De Dauw < [email protected] >
  29   */
  30  
  31  class ORMTable extends DBAccessBase implements IORMTable {
  32      /**
  33       * Cache for instances, used by the singleton method.
  34       *
  35       * @since 1.20
  36       * @deprecated since 1.21
  37       *
  38       * @var ORMTable[]
  39       */
  40      protected static $instanceCache = array();
  41  
  42      /**
  43       * @since 1.21
  44       *
  45       * @var string
  46       */
  47      protected $tableName;
  48  
  49      /**
  50       * @since 1.21
  51       *
  52       * @var string[]
  53       */
  54      protected $fields = array();
  55  
  56      /**
  57       * @since 1.21
  58       *
  59       * @var string
  60       */
  61      protected $fieldPrefix = '';
  62  
  63      /**
  64       * @since 1.21
  65       *
  66       * @var string
  67       */
  68      protected $rowClass = 'ORMRow';
  69  
  70      /**
  71       * @since 1.21
  72       *
  73       * @var array
  74       */
  75      protected $defaults = array();
  76  
  77      /**
  78       * ID of the database connection to use for read operations.
  79       * Can be changed via @see setReadDb.
  80       *
  81       * @since 1.20
  82       *
  83       * @var int DB_ enum
  84       */
  85      protected $readDb = DB_SLAVE;
  86  
  87      /**
  88       * Constructor.
  89       *
  90       * @since 1.21
  91       *
  92       * @param string $tableName
  93       * @param string[] $fields
  94       * @param array $defaults
  95       * @param string|null $rowClass
  96       * @param string $fieldPrefix
  97       */
  98  	public function __construct( $tableName = '', array $fields = array(),
  99          array $defaults = array(), $rowClass = null, $fieldPrefix = ''
 100      ) {
 101          $this->tableName = $tableName;
 102          $this->fields = $fields;
 103          $this->defaults = $defaults;
 104  
 105          if ( is_string( $rowClass ) ) {
 106              $this->rowClass = $rowClass;
 107          }
 108  
 109          $this->fieldPrefix = $fieldPrefix;
 110      }
 111  
 112      /**
 113       * @see IORMTable::getName
 114       *
 115       * @since 1.21
 116       *
 117       * @return string
 118       * @throws MWException
 119       */
 120  	public function getName() {
 121          if ( $this->tableName === '' ) {
 122              throw new MWException( 'The table name needs to be set' );
 123          }
 124  
 125          return $this->tableName;
 126      }
 127  
 128      /**
 129       * Gets the db field prefix.
 130       *
 131       * @since 1.20
 132       *
 133       * @return string
 134       */
 135  	protected function getFieldPrefix() {
 136          return $this->fieldPrefix;
 137      }
 138  
 139      /**
 140       * @see IORMTable::getRowClass
 141       *
 142       * @since 1.21
 143       *
 144       * @return string
 145       */
 146  	public function getRowClass() {
 147          return $this->rowClass;
 148      }
 149  
 150      /**
 151       * @see ORMTable::getFields
 152       *
 153       * @since 1.21
 154       *
 155       * @return array
 156       * @throws MWException
 157       */
 158  	public function getFields() {
 159          if ( $this->fields === array() ) {
 160              throw new MWException( 'The table needs to have one or more fields' );
 161          }
 162  
 163          return $this->fields;
 164      }
 165  
 166      /**
 167       * Returns a list of default field values.
 168       * field name => field value
 169       *
 170       * @since 1.20
 171       *
 172       * @return array
 173       */
 174  	public function getDefaults() {
 175          return $this->defaults;
 176      }
 177  
 178      /**
 179       * Returns a list of the summary fields.
 180       * These are fields that cache computed values, such as the amount of linked objects of $type.
 181       * This is relevant as one might not want to do actions such as log changes when these get updated.
 182       *
 183       * @since 1.20
 184       *
 185       * @return array
 186       */
 187  	public function getSummaryFields() {
 188          return array();
 189      }
 190  
 191      /**
 192       * Selects the the specified fields of the records matching the provided
 193       * conditions and returns them as DBDataObject. Field names get prefixed.
 194       *
 195       * @since 1.20
 196       *
 197       * @param array|string|null $fields
 198       * @param array $conditions
 199       * @param array $options
 200       * @param string|null $functionName
 201       *
 202       * @return ORMResult
 203       */
 204  	public function select( $fields = null, array $conditions = array(),
 205          array $options = array(), $functionName = null
 206      ) {
 207          $res = $this->rawSelect( $fields, $conditions, $options, $functionName );
 208  
 209          return new ORMResult( $this, $res );
 210      }
 211  
 212      /**
 213       * Selects the the specified fields of the records matching the provided
 214       * conditions and returns them as DBDataObject. Field names get prefixed.
 215       *
 216       * @since 1.20
 217       *
 218       * @param array|string|null $fields
 219       * @param array $conditions
 220       * @param array $options
 221       * @param string|null $functionName
 222       *
 223       * @return array Array of row objects
 224       * @throws DBQueryError If the query failed (even if the database was in ignoreErrors mode).
 225       */
 226  	public function selectObjects( $fields = null, array $conditions = array(),
 227          array $options = array(), $functionName = null
 228      ) {
 229          $result = $this->selectFields( $fields, $conditions, $options, false, $functionName );
 230  
 231          $objects = array();
 232  
 233          foreach ( $result as $record ) {
 234              $objects[] = $this->newRow( $record );
 235          }
 236  
 237          return $objects;
 238      }
 239  
 240      /**
 241       * Do the actual select.
 242       *
 243       * @since 1.20
 244       *
 245       * @param null|string|array $fields
 246       * @param array $conditions
 247       * @param array $options
 248       * @param null|string $functionName
 249       * @return ResultWrapper
 250       * @throws DBQueryError If the query failed (even if the database was in
 251       *   ignoreErrors mode).
 252       */
 253  	public function rawSelect( $fields = null, array $conditions = array(),
 254          array $options = array(), $functionName = null
 255      ) {
 256          if ( is_null( $fields ) ) {
 257              $fields = array_keys( $this->getFields() );
 258          } else {
 259              $fields = (array)$fields;
 260          }
 261  
 262          $dbr = $this->getReadDbConnection();
 263          $result = $dbr->select(
 264              $this->getName(),
 265              $this->getPrefixedFields( $fields ),
 266              $this->getPrefixedValues( $conditions ),
 267              is_null( $functionName ) ? __METHOD__ : $functionName,
 268              $options
 269          );
 270  
 271          /* @var Exception $error */
 272          $error = null;
 273  
 274          if ( $result === false ) {
 275              // Database connection was in "ignoreErrors" mode. We don't like that.
 276              // So, we emulate the DBQueryError that should have been thrown.
 277              $error = new DBQueryError(
 278                  $dbr,
 279                  $dbr->lastError(),
 280                  $dbr->lastErrno(),
 281                  $dbr->lastQuery(),
 282                  is_null( $functionName ) ? __METHOD__ : $functionName
 283              );
 284          }
 285  
 286          $this->releaseConnection( $dbr );
 287  
 288          if ( $error ) {
 289              // Note: construct the error before releasing the connection,
 290              // but throw it after.
 291              throw $error;
 292          }
 293  
 294          return $result;
 295      }
 296  
 297      /**
 298       * Selects the the specified fields of the records matching the provided
 299       * conditions and returns them as associative arrays.
 300       * Provided field names get prefixed.
 301       * Returned field names will not have a prefix.
 302       *
 303       * When $collapse is true:
 304       * If one field is selected, each item in the result array will be this field.
 305       * If two fields are selected, each item in the result array will have as key
 306       * the first field and as value the second field.
 307       * If more then two fields are selected, each item will be an associative array.
 308       *
 309       * @since 1.20
 310       *
 311       * @param array|string|null $fields
 312       * @param array $conditions
 313       * @param array $options
 314       * @param bool $collapse Set to false to always return each result row as associative array.
 315       * @param string|null $functionName
 316       *
 317       * @return array Array of array
 318       */
 319  	public function selectFields( $fields = null, array $conditions = array(),
 320          array $options = array(), $collapse = true, $functionName = null
 321      ) {
 322          $objects = array();
 323  
 324          $result = $this->rawSelect( $fields, $conditions, $options, $functionName );
 325  
 326          foreach ( $result as $record ) {
 327              $objects[] = $this->getFieldsFromDBResult( $record );
 328          }
 329  
 330          if ( $collapse ) {
 331              if ( count( $fields ) === 1 ) {
 332                  $objects = array_map( 'array_shift', $objects );
 333              } elseif ( count( $fields ) === 2 ) {
 334                  $o = array();
 335  
 336                  foreach ( $objects as $object ) {
 337                      $o[array_shift( $object )] = array_shift( $object );
 338                  }
 339  
 340                  $objects = $o;
 341              }
 342          }
 343  
 344          return $objects;
 345      }
 346  
 347      /**
 348       * Selects the the specified fields of the first matching record.
 349       * Field names get prefixed.
 350       *
 351       * @since 1.20
 352       *
 353       * @param array|string|null $fields
 354       * @param array $conditions
 355       * @param array $options
 356       * @param string|null $functionName
 357       *
 358       * @return IORMRow|bool False on failure
 359       */
 360  	public function selectRow( $fields = null, array $conditions = array(),
 361          array $options = array(), $functionName = null
 362      ) {
 363          $options['LIMIT'] = 1;
 364  
 365          $objects = $this->select( $fields, $conditions, $options, $functionName );
 366  
 367          return ( !$objects || $objects->isEmpty() ) ? false : $objects->current();
 368      }
 369  
 370      /**
 371       * Selects the the specified fields of the records matching the provided
 372       * conditions. Field names do NOT get prefixed.
 373       *
 374       * @since 1.20
 375       *
 376       * @param array $fields
 377       * @param array $conditions
 378       * @param array $options
 379       * @param string|null $functionName
 380       *
 381       * @return stdClass
 382       */
 383  	public function rawSelectRow( array $fields, array $conditions = array(),
 384          array $options = array(), $functionName = null
 385      ) {
 386          $dbr = $this->getReadDbConnection();
 387  
 388          $result = $dbr->selectRow(
 389              $this->getName(),
 390              $fields,
 391              $conditions,
 392              is_null( $functionName ) ? __METHOD__ : $functionName,
 393              $options
 394          );
 395  
 396          $this->releaseConnection( $dbr );
 397  
 398          return $result;
 399      }
 400  
 401      /**
 402       * Selects the the specified fields of the first record matching the provided
 403       * conditions and returns it as an associative array, or false when nothing matches.
 404       * This method makes use of selectFields and expects the same parameters and
 405       * returns the same results (if there are any, if there are none, this method returns false).
 406       * @see ORMTable::selectFields
 407       *
 408       * @since 1.20
 409       *
 410       * @param array|string|null $fields
 411       * @param array $conditions
 412       * @param array $options
 413       * @param bool $collapse Set to false to always return each result row as associative array.
 414       * @param string|null $functionName
 415       *
 416       * @return mixed|array|bool False on failure
 417       */
 418  	public function selectFieldsRow( $fields = null, array $conditions = array(),
 419          array $options = array(), $collapse = true, $functionName = null
 420      ) {
 421          $options['LIMIT'] = 1;
 422  
 423          $objects = $this->selectFields( $fields, $conditions, $options, $collapse, $functionName );
 424  
 425          return empty( $objects ) ? false : $objects[0];
 426      }
 427  
 428      /**
 429       * Returns if there is at least one record matching the provided conditions.
 430       * Condition field names get prefixed.
 431       *
 432       * @since 1.20
 433       *
 434       * @param array $conditions
 435       *
 436       * @return bool
 437       */
 438  	public function has( array $conditions = array() ) {
 439          return $this->selectRow( array( 'id' ), $conditions ) !== false;
 440      }
 441  
 442      /**
 443       * Checks if the table exists
 444       *
 445       * @since 1.21
 446       *
 447       * @return bool
 448       */
 449  	public function exists() {
 450          $dbr = $this->getReadDbConnection();
 451          $exists = $dbr->tableExists( $this->getName() );
 452          $this->releaseConnection( $dbr );
 453  
 454          return $exists;
 455      }
 456  
 457      /**
 458       * Returns the amount of matching records.
 459       * Condition field names get prefixed.
 460       *
 461       * Note that this can be expensive on large tables.
 462       * In such cases you might want to use DatabaseBase::estimateRowCount instead.
 463       *
 464       * @since 1.20
 465       *
 466       * @param array $conditions
 467       * @param array $options
 468       *
 469       * @return int
 470       */
 471  	public function count( array $conditions = array(), array $options = array() ) {
 472          $res = $this->rawSelectRow(
 473              array( 'rowcount' => 'COUNT(*)' ),
 474              $this->getPrefixedValues( $conditions ),
 475              $options,
 476              __METHOD__
 477          );
 478  
 479          return $res->rowcount;
 480      }
 481  
 482      /**
 483       * Removes the object from the database.
 484       *
 485       * @since 1.20
 486       *
 487       * @param array $conditions
 488       * @param string|null $functionName
 489       *
 490       * @return bool Success indicator
 491       */
 492  	public function delete( array $conditions, $functionName = null ) {
 493          $dbw = $this->getWriteDbConnection();
 494  
 495          $result = $dbw->delete(
 496              $this->getName(),
 497              $conditions === array() ? '*' : $this->getPrefixedValues( $conditions ),
 498              is_null( $functionName ) ? __METHOD__ : $functionName
 499          ) !== false; // DatabaseBase::delete does not always return true for success as documented...
 500  
 501          $this->releaseConnection( $dbw );
 502  
 503          return $result;
 504      }
 505  
 506      /**
 507       * Get API parameters for the fields supported by this object.
 508       *
 509       * @since 1.20
 510       *
 511       * @param bool $requireParams
 512       * @param bool $setDefaults
 513       *
 514       * @return array
 515       */
 516  	public function getAPIParams( $requireParams = false, $setDefaults = false ) {
 517          $typeMap = array(
 518              'id' => 'integer',
 519              'int' => 'integer',
 520              'float' => 'NULL',
 521              'str' => 'string',
 522              'bool' => 'integer',
 523              'array' => 'string',
 524              'blob' => 'string',
 525          );
 526  
 527          $params = array();
 528          $defaults = $this->getDefaults();
 529  
 530          foreach ( $this->getFields() as $field => $type ) {
 531              if ( $field == 'id' ) {
 532                  continue;
 533              }
 534  
 535              $hasDefault = array_key_exists( $field, $defaults );
 536  
 537              $params[$field] = array(
 538                  ApiBase::PARAM_TYPE => $typeMap[$type],
 539                  ApiBase::PARAM_REQUIRED => $requireParams && !$hasDefault
 540              );
 541  
 542              if ( $type == 'array' ) {
 543                  $params[$field][ApiBase::PARAM_ISMULTI] = true;
 544              }
 545  
 546              if ( $setDefaults && $hasDefault ) {
 547                  $default = is_array( $defaults[$field] )
 548                      ? implode( '|', $defaults[$field] )
 549                      : $defaults[$field];
 550                  $params[$field][ApiBase::PARAM_DFLT] = $default;
 551              }
 552          }
 553  
 554          return $params;
 555      }
 556  
 557      /**
 558       * Returns an array with the fields and their descriptions.
 559       *
 560       * field name => field description
 561       *
 562       * @since 1.20
 563       *
 564       * @return array
 565       */
 566  	public function getFieldDescriptions() {
 567          return array();
 568      }
 569  
 570      /**
 571       * Get the database ID used for read operations.
 572       *
 573       * @since 1.20
 574       *
 575       * @return int DB_ enum
 576       */
 577  	public function getReadDb() {
 578          return $this->readDb;
 579      }
 580  
 581      /**
 582       * Set the database ID to use for read operations, use DB_XXX constants or
 583       *   an index to the load balancer setup.
 584       *
 585       * @param int $db
 586       *
 587       * @since 1.20
 588       */
 589  	public function setReadDb( $db ) {
 590          $this->readDb = $db;
 591      }
 592  
 593      /**
 594       * Get the ID of the any foreign wiki to use as a target for database operations
 595       *
 596       * @since 1.20
 597       *
 598       * @return string|bool The target wiki, in a form that LBFactory understands
 599       *   (or false if the local wiki is used)
 600       */
 601  	public function getTargetWiki() {
 602          return $this->wiki;
 603      }
 604  
 605      /**
 606       * Set the ID of the any foreign wiki to use as a target for database operations
 607       *
 608       * @param string|bool $wiki The target wiki, in a form that  LBFactory
 609       *   understands (or false if the local wiki shall be used)
 610       *
 611       * @since 1.20
 612       */
 613  	public function setTargetWiki( $wiki ) {
 614          $this->wiki = $wiki;
 615      }
 616  
 617      /**
 618       * Get the database type used for read operations.
 619       * This is to be used instead of wfGetDB.
 620       *
 621       * @see LoadBalancer::getConnection
 622       *
 623       * @since 1.20
 624       *
 625       * @return DatabaseBase The database object
 626       */
 627  	public function getReadDbConnection() {
 628          return $this->getConnection( $this->getReadDb(), array() );
 629      }
 630  
 631      /**
 632       * Get the database type used for read operations.
 633       * This is to be used instead of wfGetDB.
 634       *
 635       * @see LoadBalancer::getConnection
 636       *
 637       * @since 1.20
 638       *
 639       * @return DatabaseBase The database object
 640       */
 641  	public function getWriteDbConnection() {
 642          return $this->getConnection( DB_MASTER, array() );
 643      }
 644  
 645      /**
 646       * Releases the lease on the given database connection. This is useful mainly
 647       * for connections to a foreign wiki. It does nothing for connections to the local wiki.
 648       *
 649       * @see LoadBalancer::reuseConnection
 650       *
 651       * @param DatabaseBase $db
 652       *
 653       * @since 1.20
 654       */
 655      // @codingStandardsIgnoreStart Suppress "useless method overriding" sniffer warning
 656  	public function releaseConnection( DatabaseBase $db ) {
 657          parent::releaseConnection( $db ); // just make it public
 658      }
 659      // @codingStandardsIgnoreEnd
 660  
 661      /**
 662       * Update the records matching the provided conditions by
 663       * setting the fields that are keys in the $values param to
 664       * their corresponding values.
 665       *
 666       * @since 1.20
 667       *
 668       * @param array $values
 669       * @param array $conditions
 670       *
 671       * @return bool Success indicator
 672       */
 673  	public function update( array $values, array $conditions = array() ) {
 674          $dbw = $this->getWriteDbConnection();
 675  
 676          $result = $dbw->update(
 677              $this->getName(),
 678              $this->getPrefixedValues( $values ),
 679              $this->getPrefixedValues( $conditions ),
 680              __METHOD__
 681          ) !== false; // DatabaseBase::update does not always return true for success as documented...
 682  
 683          $this->releaseConnection( $dbw );
 684  
 685          return $result;
 686      }
 687  
 688      /**
 689       * Computes the values of the summary fields of the objects matching the provided conditions.
 690       *
 691       * @since 1.20
 692       *
 693       * @param array|string|null $summaryFields
 694       * @param array $conditions
 695       */
 696  	public function updateSummaryFields( $summaryFields = null, array $conditions = array() ) {
 697          $slave = $this->getReadDb();
 698          $this->setReadDb( DB_MASTER );
 699  
 700          /**
 701           * @var IORMRow $item
 702           */
 703          foreach ( $this->select( null, $conditions ) as $item ) {
 704              $item->loadSummaryFields( $summaryFields );
 705              $item->setSummaryMode( true );
 706              $item->save();
 707          }
 708  
 709          $this->setReadDb( $slave );
 710      }
 711  
 712      /**
 713       * Takes in an associative array with field names as keys and
 714       * their values as value. The field names are prefixed with the
 715       * db field prefix.
 716       *
 717       * @since 1.20
 718       *
 719       * @param array $values
 720       *
 721       * @return array
 722       */
 723  	public function getPrefixedValues( array $values ) {
 724          $prefixedValues = array();
 725  
 726          foreach ( $values as $field => $value ) {
 727              if ( is_integer( $field ) ) {
 728                  if ( is_array( $value ) ) {
 729                      $field = $value[0];
 730                      $value = $value[1];
 731                  } else {
 732                      $value = explode( ' ', $value, 2 );
 733                      $value[0] = $this->getPrefixedField( $value[0] );
 734                      $prefixedValues[] = implode( ' ', $value );
 735                      continue;
 736                  }
 737              }
 738  
 739              $prefixedValues[$this->getPrefixedField( $field )] = $value;
 740          }
 741  
 742          return $prefixedValues;
 743      }
 744  
 745      /**
 746       * Takes in a field or array of fields and returns an
 747       * array with their prefixed versions, ready for db usage.
 748       *
 749       * @since 1.20
 750       *
 751       * @param array $fields
 752       *
 753       * @return array
 754       */
 755  	public function getPrefixedFields( array $fields ) {
 756          foreach ( $fields as &$field ) {
 757              $field = $this->getPrefixedField( $field );
 758          }
 759  
 760          return $fields;
 761      }
 762  
 763      /**
 764       * Takes in a field and returns an it's prefixed version, ready for db usage.
 765       *
 766       * @since 1.20
 767       *
 768       * @param string|array $field
 769       *
 770       * @return string
 771       */
 772  	public function getPrefixedField( $field ) {
 773          return $this->getFieldPrefix() . $field;
 774      }
 775  
 776      /**
 777       * Takes an array of field names with prefix and returns the unprefixed equivalent.
 778       *
 779       * @since 1.20
 780       *
 781       * @param array $fieldNames
 782       *
 783       * @return array
 784       */
 785  	public function unprefixFieldNames( array $fieldNames ) {
 786          return array_map( array( $this, 'unprefixFieldName' ), $fieldNames );
 787      }
 788  
 789      /**
 790       * Takes a field name with prefix and returns the unprefixed equivalent.
 791       *
 792       * @since 1.20
 793       *
 794       * @param string $fieldName
 795       *
 796       * @return string
 797       */
 798  	public function unprefixFieldName( $fieldName ) {
 799          return substr( $fieldName, strlen( $this->getFieldPrefix() ) );
 800      }
 801  
 802      /**
 803       * Get an instance of this class.
 804       *
 805       * @since 1.20
 806       * @deprecated since 1.21
 807       *
 808       * @return IORMTable
 809       */
 810  	public static function singleton() {
 811          $class = get_called_class();
 812  
 813          if ( !array_key_exists( $class, self::$instanceCache ) ) {
 814              self::$instanceCache[$class] = new $class;
 815          }
 816  
 817          return self::$instanceCache[$class];
 818      }
 819  
 820      /**
 821       * Get an array with fields from a database result,
 822       * that can be fed directly to the constructor or
 823       * to setFields.
 824       *
 825       * @since 1.20
 826       *
 827       * @param stdClass $result
 828       * @throws MWException
 829       * @return array
 830       */
 831  	public function getFieldsFromDBResult( stdClass $result ) {
 832          $result = (array)$result;
 833  
 834          $rawFields = array_combine(
 835              $this->unprefixFieldNames( array_keys( $result ) ),
 836              array_values( $result )
 837          );
 838  
 839          $fieldDefinitions = $this->getFields();
 840          $fields = array();
 841  
 842          foreach ( $rawFields as $name => $value ) {
 843              if ( array_key_exists( $name, $fieldDefinitions ) ) {
 844                  switch ( $fieldDefinitions[$name] ) {
 845                      case 'int':
 846                          $value = (int)$value;
 847                          break;
 848                      case 'float':
 849                          $value = (float)$value;
 850                          break;
 851                      case 'bool':
 852                          if ( is_string( $value ) ) {
 853                              $value = $value !== '0';
 854                          } elseif ( is_int( $value ) ) {
 855                              $value = $value !== 0;
 856                          }
 857                          break;
 858                      case 'array':
 859                          if ( is_string( $value ) ) {
 860                              $value = unserialize( $value );
 861                          }
 862  
 863                          if ( !is_array( $value ) ) {
 864                              $value = array();
 865                          }
 866                          break;
 867                      case 'blob':
 868                          if ( is_string( $value ) ) {
 869                              $value = unserialize( $value );
 870                          }
 871                          break;
 872                      case 'id':
 873                          if ( is_string( $value ) ) {
 874                              $value = (int)$value;
 875                          }
 876                          break;
 877                  }
 878  
 879                  $fields[$name] = $value;
 880              } else {
 881                  throw new MWException( 'Attempted to set unknown field ' . $name );
 882              }
 883          }
 884  
 885          return $fields;
 886      }
 887  
 888      /**
 889       * @see ORMTable::newRowFromFromDBResult
 890       *
 891       * @deprecated since 1.20 use newRowFromDBResult instead
 892       * @since 1.20
 893       *
 894       * @param stdClass $result
 895       *
 896       * @return IORMRow
 897       */
 898  	public function newFromDBResult( stdClass $result ) {
 899          return self::newRowFromDBResult( $result );
 900      }
 901  
 902      /**
 903       * Get a new instance of the class from a database result.
 904       *
 905       * @since 1.20
 906       *
 907       * @param stdClass $result
 908       *
 909       * @return IORMRow
 910       */
 911  	public function newRowFromDBResult( stdClass $result ) {
 912          return $this->newRow( $this->getFieldsFromDBResult( $result ) );
 913      }
 914  
 915      /**
 916       * @see ORMTable::newRow
 917       *
 918       * @deprecated since 1.20 use newRow instead
 919       * @since 1.20
 920       *
 921       * @param array $data
 922       * @param bool $loadDefaults
 923       *
 924       * @return IORMRow
 925       */
 926  	public function newFromArray( array $data, $loadDefaults = false ) {
 927          return static::newRow( $data, $loadDefaults );
 928      }
 929  
 930      /**
 931       * Get a new instance of the class from an array.
 932       *
 933       * @since 1.20
 934       *
 935       * @param array $fields
 936       * @param bool $loadDefaults
 937       *
 938       * @return IORMRow
 939       */
 940  	public function newRow( array $fields, $loadDefaults = false ) {
 941          $class = $this->getRowClass();
 942  
 943          return new $class( $this, $fields, $loadDefaults );
 944      }
 945  
 946      /**
 947       * Return the names of the fields.
 948       *
 949       * @since 1.20
 950       *
 951       * @return array
 952       */
 953  	public function getFieldNames() {
 954          return array_keys( $this->getFields() );
 955      }
 956  
 957      /**
 958       * Gets if the object can take a certain field.
 959       *
 960       * @since 1.20
 961       *
 962       * @param string $name
 963       *
 964       * @return bool
 965       */
 966  	public function canHaveField( $name ) {
 967          return array_key_exists( $name, $this->getFields() );
 968      }
 969  
 970      /**
 971       * Updates the provided row in the database.
 972       *
 973       * @since 1.22
 974       *
 975       * @param IORMRow $row The row to save
 976       * @param string|null $functionName
 977       *
 978       * @return bool Success indicator
 979       */
 980  	public function updateRow( IORMRow $row, $functionName = null ) {
 981          $dbw = $this->getWriteDbConnection();
 982  
 983          $success = $dbw->update(
 984              $this->getName(),
 985              $this->getWriteValues( $row ),
 986              $this->getPrefixedValues( array( 'id' => $row->getId() ) ),
 987              is_null( $functionName ) ? __METHOD__ : $functionName
 988          );
 989  
 990          $this->releaseConnection( $dbw );
 991  
 992          // DatabaseBase::update does not always return true for success as documented...
 993          return $success !== false;
 994      }
 995  
 996      /**
 997       * Inserts the provided row into the database.
 998       *
 999       * @since 1.22
1000       *
1001       * @param IORMRow $row
1002       * @param string|null $functionName
1003       * @param array|null $options
1004       *
1005       * @return bool Success indicator
1006       */
1007  	public function insertRow( IORMRow $row, $functionName = null, array $options = null ) {
1008          $dbw = $this->getWriteDbConnection();
1009  
1010          $success = $dbw->insert(
1011              $this->getName(),
1012              $this->getWriteValues( $row ),
1013              is_null( $functionName ) ? __METHOD__ : $functionName,
1014              $options
1015          );
1016  
1017          // DatabaseBase::insert does not always return true for success as documented...
1018          $success = $success !== false;
1019  
1020          if ( $success ) {
1021              $row->setField( 'id', $dbw->insertId() );
1022          }
1023  
1024          $this->releaseConnection( $dbw );
1025  
1026          return $success;
1027      }
1028  
1029      /**
1030       * Gets the fields => values to write to the table.
1031       *
1032       * @since 1.22
1033       *
1034       * @param IORMRow $row
1035       *
1036       * @return array
1037       */
1038  	protected function getWriteValues( IORMRow $row ) {
1039          $values = array();
1040  
1041          $rowFields = $row->getFields();
1042  
1043          foreach ( $this->getFields() as $name => $type ) {
1044              if ( array_key_exists( $name, $rowFields ) ) {
1045                  $value = $rowFields[$name];
1046  
1047                  switch ( $type ) {
1048                      case 'array':
1049                          $value = (array)$value;
1050                      // fall-through!
1051                      case 'blob':
1052                          $value = serialize( $value );
1053                      // fall-through!
1054                  }
1055  
1056                  $values[$this->getPrefixedField( $name )] = $value;
1057              }
1058          }
1059  
1060          return $values;
1061      }
1062  
1063      /**
1064       * Removes the provided row from the database.
1065       *
1066       * @since 1.22
1067       *
1068       * @param IORMRow $row
1069       * @param string|null $functionName
1070       *
1071       * @return bool Success indicator
1072       */
1073  	public function removeRow( IORMRow $row, $functionName = null ) {
1074          $success = $this->delete(
1075              array( 'id' => $row->getId() ),
1076              is_null( $functionName ) ? __METHOD__ : $functionName
1077          );
1078  
1079          // DatabaseBase::delete does not always return true for success as documented...
1080          return $success !== false;
1081      }
1082  
1083      /**
1084       * Add an amount (can be negative) to the specified field (needs to be numeric).
1085       *
1086       * @since 1.22
1087       *
1088       * @param array $conditions
1089       * @param string $field
1090       * @param int $amount
1091       *
1092       * @return bool Success indicator
1093       * @throws MWException
1094       */
1095  	public function addToField( array $conditions, $field, $amount ) {
1096          if ( !array_key_exists( $field, $this->fields ) ) {
1097              throw new MWException( 'Unknown field "' . $field . '" provided' );
1098          }
1099  
1100          if ( $amount == 0 ) {
1101              return true;
1102          }
1103  
1104          $absoluteAmount = abs( $amount );
1105          $isNegative = $amount < 0;
1106  
1107          $fullField = $this->getPrefixedField( $field );
1108  
1109          $dbw = $this->getWriteDbConnection();
1110  
1111          $success = $dbw->update(
1112              $this->getName(),
1113              array( "$fullField=$fullField" . ( $isNegative ? '-' : '+' ) . $absoluteAmount ),
1114              $this->getPrefixedValues( $conditions ),
1115              __METHOD__
1116          ) !== false; // DatabaseBase::update does not always return true for success as documented...
1117  
1118          $this->releaseConnection( $dbw );
1119  
1120          return $success;
1121      }
1122  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1