Current File : /home/jvzmxxx/wiki/extensions/Flow/includes/Data/Mapper/CachingObjectMapper.php
<?php

namespace Flow\Data\Mapper;

use Flow\Data\ObjectManager;
use Flow\Data\ObjectMapper;
use Flow\Data\Utils\MultiDimArray;
use Flow\Model\UUID;
use InvalidArgumentException;
use OutOfBoundsException;

/**
 * Rows with the same primary key always return the same object
 * from self::fromStorageRow.  This means that if two parts of the
 * code both load revision 123 they will receive the same object.
 */
class CachingObjectMapper implements ObjectMapper {
	/**
	 * @var callable
	 */
	protected $toStorageRow;

	/**
	 * @var callable
	 */
	protected $fromStorageRow;

	/**
	 * @var string[]
	 */
	protected $primaryKey;

	/**
	 * @var MultiDimArray
	 */
	protected $loaded;

	/**
	 * @param callable $toStorageRow
	 * @param callable $fromStorageRow
	 * @param string[] $primaryKey
	 */
	public function __construct( $toStorageRow, $fromStorageRow, array $primaryKey ) {
		$this->toStorageRow = $toStorageRow;
		$this->fromStorageRow = $fromStorageRow;
		ksort( $primaryKey );
		$this->primaryKey = $primaryKey;
		$this->clear();
	}

	/**
	 * @param string $className Fully qualified class name
	 * @param string[] $primaryKey
	 * @return CachingObjectMapper
	 */
	static public function model( $className, array $primaryKey ) {
		return new self(
			array( $className, 'toStorageRow' ),
			array( $className, 'fromStorageRow' ),
			$primaryKey
		);
	}

	public function toStorageRow( $object ) {
		$row = call_user_func( $this->toStorageRow, $object );
		$pk = ObjectManager::splitFromRow( $row, $this->primaryKey );
		if ( $pk === null ) {
			// new object may not have pk yet, calling code
			// should call self::fromStorageRow with $object to load
			// db assigned pk and store obj in $this->loaded
		} elseif ( !isset( $this->loaded[$pk] ) ) {
			// first time this id has been seen
			$this->loaded[$pk] = $object;
		} elseif ( $this->loaded[$pk] !== $object ) {
			// loaded object of this id is not same object
			$class = get_class( $object );
			$id = json_encode( $pk );
			throw new \InvalidArgumentException( "Duplicate '$class' objects for id $id" );
		}
		return $row;
	}

	public function fromStorageRow( array $row, $object = null ) {
		$pk = ObjectManager::splitFromRow( $row, $this->primaryKey );
		if ( $pk === null ) {
			throw new \InvalidArgumentException( 'Storage row has no pk' );
		} elseif ( !isset( $this->loaded[$pk] ) ) {
			// unserialize the object
			return $this->loaded[$pk] = call_user_func( $this->fromStorageRow, $row, $object );
		} elseif ( $object === null ) {
			// provide previously loaded object
			return $this->loaded[$pk];
		} elseif ( $object !== $this->loaded[$pk] ) {
			// loaded object of this id is not same object
			$class = get_class( $object );
			$id = json_encode( $pk );
			throw new \InvalidArgumentException( "Duplicate '$class' objects for id $id" );
		} else {
			// object was provided, load $row into $object
			// we already know $this->loaded[$pk] === $object
			return call_user_func( $this->fromStorageRow, $row, $object );
		}
	}

	/**
	 * @param array $primaryKey
	 * @return object|null
	 * @throws InvalidArgumentException
	 */
	public function get( array $primaryKey ) {
		$primaryKey = UUID::convertUUIDs( $primaryKey, 'alphadecimal' );
		ksort( $primaryKey );
		if ( array_keys( $primaryKey ) !== $this->primaryKey ) {
			throw new InvalidArgumentException;
		}
		try {
			return $this->loaded[$primaryKey];
		} catch ( OutOfBoundsException $e ) {
			return null;
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public function normalizeRow( array $row ) {
		$object = call_user_func( $this->fromStorageRow, $row );
		return call_user_func( $this->toStorageRow, $object );
	}

	public function clear() {
		$this->loaded = new MultiDimArray;
	}
}