| Current File : /home/jvzmxxx/wiki1/extensions/Flow/includes/Collection/LocalCacheAbstractCollection.php |
<?php
namespace Flow\Collection;
use Flow\Exception\InvalidDataException;
use Flow\Model\AbstractRevision;
use Flow\Model\UUID;
/**
* LocalBufferedCache saves all data that has been requested in an internal
* cache (in memory, per request). This provides the opportunity of (trying to)
* be smart about what results we fetch.
* The class extends the default AbstractCollection to make sure not all
* revisions are loaded unless we really need them. It could very well be that
* perhaps 5 recent revisions have already been loaded in other parts of the
* code, and we only need the 3rd most recent, in which case we shouldn't
* try to fetch all of them.
*/
abstract class LocalCacheAbstractCollection extends AbstractCollection {
/**
* Returns all revisions.
*
* @return AbstractRevision[]
*/
public function getAllRevisions() {
// if we have not yet loaded everything, just clear what we have and
// fetch from cache
if ( !$this->loaded() ) {
$this->revisions = array();
}
return parent::getAllRevisions();
}
/**
* Returns the revision with the given id.
*
* @param UUID $uuid
* @return AbstractRevision|null null if there is no such revision
*/
public function getRevision( UUID $uuid ) {
// check if fetching last already res
if ( isset( $this->revisions[$uuid->getAlphadecimal() ] ) ) {
return $this->revisions[$uuid->getAlphadecimal() ];
}
/*
* The strategy here is to avoid having to call getAllRevisions(), which
* is most likely to have to load (fresh) data that is not yet in
* LocalBufferedCache's internal cache.
* To do so, we'll build the $this->revisions array by hand. Starting at
* the most recent revision and going up 1 revision at a time, checking
* if it is already in LocalBufferedCache's cache.
* If, however, we can't find the requested revisions (or one of the
* revisions on our way to the requested revision) in the internal cache
* of LocalBufferedCache, we'll just bail and load all revisions after
* all: if we do have to fetch data, might as well do it all in 1 go!
*/
while ( !$this->loaded() ) {
// fetch current oldest revision
$oldest = $this->getOldestLoaded();
// fetch that one's preceding revision id
$previousId = $oldest->getPrevRevisionId();
// check if it's in local storage already
if ( $previousId && self::getStorage()->got( $previousId ) ) {
$revision = self::getStorage()->get( $previousId );
// add this revision to revisions array
$this->revisions[$previousId->getAlphadecimal()] = $revision;
// stop iterating if we've found the one we wanted
if ( $uuid->equals( $previousId ) ) {
break;
}
} else {
// revision not found in local storage: load all revisions
$this->getAllRevisions();
break;
}
}
if ( !isset( $this->revisions[$uuid->getAlphadecimal()] ) ) {
return null;
}
return $this->revisions[$uuid->getAlphadecimal()];
}
/**
* Returns the most recent revision.
*
* @return AbstractRevision
* @throws InvalidDataException When no revision can be located
*/
public function getLastRevision() {
// if $revisions is not empty, it will always have the last revision,
// at the beginning of the array
if ( $this->revisions ) {
return reset( $this->revisions );
}
$attributes = array( 'rev_type_id' => $this->uuid );
$options = array( 'sort' => 'rev_id', 'limit' => 1, 'order' => 'DESC' );
if ( self::getStorage()->found( $attributes, $options ) ) {
// if last revision is already known in local cache, fetch it
$revision = self::getStorage()->find( $attributes, $options );
if ( !$revision ) {
throw new InvalidDataException( 'Last revision for ' . $this->uuid->getAlphadecimal() . ' could not be found', 'invalid-type-id' );
}
$revision = reset( $revision );
$this->revisions[$revision->getRevisionId()->getAlphadecimal()] = $revision;
return $revision;
} else {
// otherwise, might as well fetch all previous revisions while we're at
// it - saves roundtrips to cache/db
$this->getAllRevisions();
return reset( $this->revisions );
}
}
/**
* Given a certain revision, returns the next revision.
*
* @param AbstractRevision $revision
* @return AbstractRevision|null null if there is no next revision
*/
public function getNextRevision( AbstractRevision $revision ) {
// make sure the given revision is loaded
$this->getRevision( $revision->getRevisionId() );
// find requested id, based on given revision
$ids = array_keys( $this->revisions );
$current = array_search( $revision->getRevisionId()->getAlphadecimal(), $ids );
$next = $current - 1;
if ( $next < 0 ) {
return null;
}
return $this->getRevision( UUID::create( $ids[$next] ) );
}
/**
* Returns true if all revisions have been loaded into $this->revisions.
*
* @return bool
*/
public function loaded() {
$first = end( $this->revisions );
return $first && $first->getPrevRevisionId() === null;
}
/**
* Returns the oldest revision that has already been fetched via this class.
*
* @return AbstractRevision
*/
public function getOldestLoaded() {
if ( !$this->revisions ) {
return $this->getLastRevision();
}
return end( $this->revisions );
}
}