Current File : /home/jvzmxxx/wiki1/extensions/Wikibase/client/includes/Usage/Sql/SqlSubscriptionManager.php
<?php

namespace Wikibase\Client\Usage\Sql;

use DatabaseBase;
use DBError;
use Exception;
use InvalidArgumentException;
use ResultWrapper;
use Wikibase\Client\Store\Sql\ConsistentReadConnectionManager;
use Wikibase\Client\Usage\SubscriptionManager;
use Wikibase\Client\Usage\UsageTrackerException;
use Wikibase\DataModel\Entity\EntityId;

/**
 * SubscriptionManager implementation backed by an SQL table.
 *
 * @see docs/usagetracking.wiki
 *
 * @license GPL-2.0+
 * @author Daniel Kinzler
 */
class SqlSubscriptionManager implements SubscriptionManager {

	/**
	 * @var ConsistentReadConnectionManager
	 */
	private $connectionManager;

	/**
	 * @param ConsistentReadConnectionManager $connectionManager
	 */
	public function __construct( ConsistentReadConnectionManager $connectionManager ) {
		$this->connectionManager = $connectionManager;
	}

	/**
	 * @param EntityId[] $entityIds
	 *
	 * @return string[]
	 */
	private function idsToString( array $entityIds ) {
		return array_map( function( EntityId $id ) {
			return $id->getSerialization();
		}, $entityIds );
	}

	/**
	 * @see SubscriptionManager::subscribe
	 *
	 * @param string $subscriber
	 * @param EntityId[] $entityIds
	 *
	 * @throws InvalidArgumentException
	 * @throws UsageTrackerException
	 * @throws Exception
	 */
	public function subscribe( $subscriber, array $entityIds ) {
		if ( !is_string( $subscriber ) ) {
			throw new InvalidArgumentException( '$subscriber must be a string.' );
		}

		$subscriptions = $this->idsToString( $entityIds );
		$db = $this->connectionManager->beginAtomicSection( __METHOD__ );

		try {
			$oldSubscriptions = $this->querySubscriptions( $db, $subscriber, $subscriptions );
			$newSubscriptions = array_diff( $subscriptions, $oldSubscriptions );
			$this->insertSubscriptions( $db, $subscriber, $newSubscriptions );

			$this->connectionManager->commitAtomicSection( $db, __METHOD__ );
		} catch ( Exception $ex ) {
			$this->connectionManager->rollbackAtomicSection( $db, __METHOD__ );

			if ( $ex instanceof DBError ) {
				throw new UsageTrackerException( $ex->getMessage(), $ex->getCode(), $ex );
			} else {
				throw $ex;
			}
		}
	}

	/**
	 * @see SubscriptionManager::unsubscribe
	 *
	 * @param string $subscriber Global site ID of the client
	 * @param EntityId[] $entityIds The entities to subscribe to.
	 *
	 * @throws InvalidArgumentException
	 * @throws UsageTrackerException
	 * @throws Exception
	 */
	public function unsubscribe( $subscriber, array $entityIds ) {
		if ( !is_string( $subscriber ) ) {
			throw new InvalidArgumentException( '$subscriber must be a string.' );
		}

		$unsubscriptions = $this->idsToString( $entityIds );
		$db = $this->connectionManager->beginAtomicSection( __METHOD__ );

		try {
			$oldSubscriptions = $this->querySubscriptions( $db, $subscriber, $unsubscriptions );
			$obsoleteSubscriptions = array_intersect( $unsubscriptions, $oldSubscriptions );
			$this->deleteSubscriptions( $db, $subscriber, $obsoleteSubscriptions );

			$this->connectionManager->commitAtomicSection( $db, __METHOD__ );
		} catch ( Exception $ex ) {
			$this->connectionManager->rollbackAtomicSection( $db, __METHOD__ );

			if ( $ex instanceof DBError ) {
				throw new UsageTrackerException( $ex->getMessage(), $ex->getCode(), $ex );
			} else {
				throw $ex;
			}
		}
	}

	/**
	 * For a set of potential subscriptions, returns the existing subscriptions.
	 *
	 * @param DatabaseBase $db
	 * @param string $subscriber
	 * @param string[] $subscriptions
	 *
	 * @return string[] Entity ID strings from $subscriptions which $subscriber is already subscribed to.
	 */
	private function querySubscriptions( DatabaseBase $db, $subscriber, array $subscriptions ) {
		if ( $subscriptions ) {
			$rows = $db->select(
				'wb_changes_subscription',
				'cs_entity_id',
				array(
					'cs_subscriber_id' => $subscriber,
					'cs_entity_id' => $subscriptions,
				),
				__METHOD__
			);

			$subscriptions = $this->extractField( $rows, 'cs_entity_id' );
		}

		return $subscriptions;
	}

	/**
	 * Inserts a set of subscriptions.
	 *
	 * @param DatabaseBase $db
	 * @param string $subscriber
	 * @param string[] $subscriptions
	 */
	private function insertSubscriptions( DatabaseBase $db, $subscriber, array $subscriptions ) {
		$rows = $this->makeSubscriptionRows( $subscriber, $subscriptions );

		$db->insert(
			'wb_changes_subscription',
			$rows,
			__METHOD__,
			array( 'IGNORE' )
		);
	}

	/**
	 * Inserts a set of subscriptions.
	 *
	 * @param DatabaseBase $db
	 * @param string $subscriber
	 * @param string[] $subscriptions
	 */
	private function deleteSubscriptions( DatabaseBase $db, $subscriber, array $subscriptions ) {
		if ( $subscriptions ) {
			$db->delete(
				'wb_changes_subscription',
				array(
					'cs_subscriber_id' => $subscriber,
					'cs_entity_id' => $subscriptions,
				),
				__METHOD__
			);
		}
	}

	/**
	 * Returns a list of rows for insertion, using DatabaseBase's multi-row insert mechanism.
	 * Each row is represented as array( $subscriber, $entityId ).
	 *
	 * @param string $subscriber
	 * @param string[] $subscriptions
	 *
	 * @return array[] rows
	 */
	private function makeSubscriptionRows( $subscriber, array $subscriptions ) {
		$rows = array();

		foreach ( $subscriptions as $entityId ) {
			$rows[] = array(
				'cs_entity_id' => $entityId,
				'cs_subscriber_id' => $subscriber
			);
		}

		return $rows;
	}

	/**
	 * @param object[]|ResultWrapper $rows Plain objects
	 * @param string $field The name of the field to extract from each plain object
	 *
	 * @return array
	 */
	private function extractField( $rows, $field ) {
		$values = array();

		foreach ( $rows as $row ) {
			$values[] = $row->$field;
		}

		return $values;
	}

}