2015-06-09 10:14:00 +00:00
|
|
|
<?php
|
|
|
|
|
2018-08-09 09:49:10 +00:00
|
|
|
use PortableInfobox\Helpers\PagePropsProxy;
|
|
|
|
use PortableInfobox\Helpers\PortableInfoboxParsingHelper;
|
|
|
|
use PortableInfobox\Parser\Nodes\NodeInfobox;
|
2015-09-22 15:48:38 +00:00
|
|
|
|
2015-06-09 10:14:00 +00:00
|
|
|
class PortableInfoboxDataService {
|
|
|
|
|
2018-08-08 08:29:34 +00:00
|
|
|
const CACHE_TTL = 86400; // 24 hours
|
2015-06-09 10:14:00 +00:00
|
|
|
const IMAGE_FIELD_TYPE = 'image';
|
2015-07-02 07:54:06 +00:00
|
|
|
const INFOBOXES_PROPERTY_NAME = 'infoboxes';
|
2015-06-09 10:14:00 +00:00
|
|
|
|
2015-06-22 15:15:26 +00:00
|
|
|
protected $title;
|
2016-12-14 11:31:25 +00:00
|
|
|
protected $parsingHelper;
|
2015-09-23 13:51:41 +00:00
|
|
|
protected $propsProxy;
|
2015-09-22 15:48:38 +00:00
|
|
|
protected $cache;
|
2018-08-02 15:56:59 +00:00
|
|
|
protected $memcached;
|
2015-09-22 15:48:38 +00:00
|
|
|
protected $cachekey;
|
2015-06-22 15:15:26 +00:00
|
|
|
|
2015-09-22 15:48:38 +00:00
|
|
|
/**
|
|
|
|
* @param $title Title
|
2015-09-23 13:51:41 +00:00
|
|
|
*
|
|
|
|
* @internal param $helper
|
2015-09-22 15:48:38 +00:00
|
|
|
*/
|
2015-09-23 13:51:41 +00:00
|
|
|
protected function __construct( $title ) {
|
|
|
|
$this->title = $title !== null ? $title : new Title();
|
2016-12-14 11:31:25 +00:00
|
|
|
$this->parsingHelper = new PortableInfoboxParsingHelper();
|
2015-09-23 13:51:41 +00:00
|
|
|
$this->propsProxy = new PagePropsProxy();
|
2018-08-02 15:56:59 +00:00
|
|
|
$this->memcached = ObjectCache::getMainWANInstance();
|
|
|
|
$this->cachekey = $this->memcached->makeKey(
|
2016-12-09 15:55:42 +00:00
|
|
|
__CLASS__,
|
|
|
|
$this->title->getArticleID(),
|
|
|
|
self::INFOBOXES_PROPERTY_NAME,
|
|
|
|
PortableInfoboxParserTagController::PARSER_TAG_VERSION
|
|
|
|
);
|
2015-06-22 15:15:26 +00:00
|
|
|
}
|
|
|
|
|
2015-09-23 13:51:41 +00:00
|
|
|
public static function newFromTitle( $title ) {
|
|
|
|
return new PortableInfoboxDataService( $title );
|
2015-06-23 08:41:15 +00:00
|
|
|
}
|
|
|
|
|
2015-09-23 13:51:41 +00:00
|
|
|
public static function newFromPageID( $pageid ) {
|
|
|
|
return new PortableInfoboxDataService( Title::newFromID( $pageid ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
// set internal helpers methods
|
2016-12-14 11:31:25 +00:00
|
|
|
public function setParsingHelper( $helper ) {
|
|
|
|
$this->parsingHelper = $helper;
|
2015-09-23 13:51:41 +00:00
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setPagePropsProxy( $proxy ) {
|
|
|
|
$this->propsProxy = $proxy;
|
|
|
|
|
|
|
|
return $this;
|
2015-06-22 15:15:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-09-24 14:25:27 +00:00
|
|
|
* Returns infobox data, chain terminator method
|
2015-06-22 15:15:26 +00:00
|
|
|
*
|
2016-12-09 14:56:57 +00:00
|
|
|
* @return array in format [ [ 'data' => [], 'metadata' => [] ] or [] will be returned
|
2015-06-22 15:15:26 +00:00
|
|
|
*/
|
|
|
|
public function getData() {
|
2015-09-22 15:48:38 +00:00
|
|
|
if ( $this->title && $this->title->exists() && $this->title->inNamespace( NS_TEMPLATE ) ) {
|
2016-12-14 11:31:25 +00:00
|
|
|
$incOnlyTemplates = $this->parsingHelper->parseIncludeonlyInfoboxes( $this->title );
|
2018-08-02 16:05:29 +00:00
|
|
|
$this->delete();
|
|
|
|
$this->set( $incOnlyTemplates );
|
2015-06-09 10:14:00 +00:00
|
|
|
}
|
2015-09-23 13:51:41 +00:00
|
|
|
$result = $this->get();
|
2015-06-22 15:15:26 +00:00
|
|
|
|
2015-09-23 13:51:41 +00:00
|
|
|
return $result !== null ? $result : [ ];
|
2015-06-09 10:14:00 +00:00
|
|
|
}
|
|
|
|
|
2016-02-16 12:37:48 +00:00
|
|
|
/**
|
2016-12-19 12:26:18 +00:00
|
|
|
* @return array of strings (infobox markups)
|
|
|
|
*/
|
2016-02-16 12:37:48 +00:00
|
|
|
public function getInfoboxes() {
|
2016-12-14 11:31:25 +00:00
|
|
|
return $this->parsingHelper->getMarkup( $this->title );
|
2016-02-16 12:37:48 +00:00
|
|
|
}
|
|
|
|
|
2015-06-09 12:31:04 +00:00
|
|
|
/**
|
2015-12-07 22:36:56 +00:00
|
|
|
* Get image list from multiple infoboxes data
|
2015-06-22 15:15:26 +00:00
|
|
|
*
|
2015-06-09 12:31:04 +00:00
|
|
|
* @return array
|
|
|
|
*/
|
2015-06-22 15:15:26 +00:00
|
|
|
public function getImages() {
|
2016-12-19 12:26:18 +00:00
|
|
|
$images = [ ];
|
2015-06-22 15:15:26 +00:00
|
|
|
foreach ( $this->getData() as $infobox ) {
|
2015-12-07 22:36:56 +00:00
|
|
|
if ( is_array( $infobox[ 'data' ] ) ) {
|
|
|
|
$images = array_merge( $images, $this->getImageFromOneInfoboxData( $infobox[ 'data' ] ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return array_unique( $images );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get image list from single infobox data
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
private function getImageFromOneInfoboxData( $infoboxData ) {
|
2016-12-19 12:26:18 +00:00
|
|
|
$images = [ ];
|
2015-12-07 22:36:56 +00:00
|
|
|
foreach ( $infoboxData as $infoboxDataField ) {
|
2015-12-07 22:37:56 +00:00
|
|
|
if ( $infoboxDataField[ 'type' ] === self::IMAGE_FIELD_TYPE && isset( $infoboxDataField[ 'data' ] ) ) {
|
2015-12-07 22:36:56 +00:00
|
|
|
$images = array_merge( $images, $this->getImagesFromOneNodeImageData( $infoboxDataField[ 'data' ] ) );
|
2015-06-09 10:14:00 +00:00
|
|
|
}
|
|
|
|
}
|
2015-12-07 22:36:56 +00:00
|
|
|
return $images;
|
|
|
|
}
|
2015-06-22 15:15:26 +00:00
|
|
|
|
2015-12-07 22:36:56 +00:00
|
|
|
/**
|
|
|
|
* Get image list from single NodeImage data
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
private function getImagesFromOneNodeImageData( $nodeImageData ) {
|
2016-12-19 12:26:18 +00:00
|
|
|
$images = [ ];
|
2015-12-07 22:36:56 +00:00
|
|
|
for ( $i = 0; $i < count( $nodeImageData ); $i++ ) {
|
|
|
|
if ( !empty( $nodeImageData[ $i ] [ 'key' ] ) ) {
|
|
|
|
$images[] = $nodeImageData[ $i ][ 'key' ];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $images;
|
2015-06-09 10:14:00 +00:00
|
|
|
}
|
2015-08-19 20:00:27 +00:00
|
|
|
|
|
|
|
/**
|
2015-09-22 15:48:38 +00:00
|
|
|
* Save infobox data, permanently
|
2016-12-09 15:55:42 +00:00
|
|
|
* NOTICE: This method isn't currently used anywhere
|
2015-09-22 15:48:38 +00:00
|
|
|
*
|
|
|
|
* @param NodeInfobox $raw infobox parser output
|
2015-09-23 13:51:41 +00:00
|
|
|
*
|
|
|
|
* @return $this
|
2015-08-19 20:00:27 +00:00
|
|
|
*/
|
2015-09-22 15:48:38 +00:00
|
|
|
public function save( NodeInfobox $raw ) {
|
|
|
|
if ( $raw ) {
|
|
|
|
$stored = $this->get();
|
2016-12-07 18:02:00 +00:00
|
|
|
$stored[] = [
|
2016-12-14 12:54:53 +00:00
|
|
|
'parser_tag_version' => PortableInfoboxParserTagController::PARSER_TAG_VERSION,
|
2016-12-07 18:02:00 +00:00
|
|
|
'data' => $raw->getRenderData(),
|
2016-12-09 15:55:42 +00:00
|
|
|
'metadata' => $raw->getMetadata()
|
2016-12-07 18:02:00 +00:00
|
|
|
];
|
2015-09-22 15:48:38 +00:00
|
|
|
$this->set( $stored );
|
2015-08-19 20:00:27 +00:00
|
|
|
}
|
2015-09-23 13:51:41 +00:00
|
|
|
|
|
|
|
return $this;
|
2015-08-19 20:00:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-09-22 15:48:38 +00:00
|
|
|
* Remove infobox data from page props and memcache
|
2015-08-19 20:00:27 +00:00
|
|
|
*/
|
2015-09-22 15:48:38 +00:00
|
|
|
public function delete() {
|
|
|
|
$this->clear();
|
|
|
|
unset( $this->cache );
|
2015-09-23 13:51:41 +00:00
|
|
|
|
|
|
|
return $this;
|
2015-08-19 20:00:27 +00:00
|
|
|
}
|
2015-08-20 13:52:36 +00:00
|
|
|
|
|
|
|
/**
|
2015-09-22 15:48:38 +00:00
|
|
|
* Purge mem cache and local cache
|
2015-08-20 13:52:36 +00:00
|
|
|
*/
|
2015-09-22 15:48:38 +00:00
|
|
|
public function purge() {
|
2018-08-02 15:56:59 +00:00
|
|
|
$this->memcached->delete( $this->cachekey );
|
2015-09-22 15:48:38 +00:00
|
|
|
unset( $this->cache );
|
2015-09-23 13:51:41 +00:00
|
|
|
|
|
|
|
return $this;
|
2015-09-22 15:48:38 +00:00
|
|
|
}
|
2015-08-20 13:52:36 +00:00
|
|
|
|
2015-09-22 15:48:38 +00:00
|
|
|
// soft cache handlers
|
|
|
|
protected function get() {
|
|
|
|
if ( !isset( $this->cache ) ) {
|
|
|
|
$this->cache = $this->load();
|
2015-08-25 12:27:01 +00:00
|
|
|
}
|
|
|
|
|
2015-09-22 15:48:38 +00:00
|
|
|
return $this->cache;
|
|
|
|
}
|
2015-08-25 12:27:01 +00:00
|
|
|
|
2015-09-22 15:48:38 +00:00
|
|
|
protected function set( $data ) {
|
|
|
|
$this->store( $data );
|
|
|
|
$this->cache = $data;
|
2015-08-25 12:27:01 +00:00
|
|
|
}
|
|
|
|
|
2015-09-22 15:48:38 +00:00
|
|
|
// PageProps handlers with memcache wrappers
|
|
|
|
protected function load() {
|
|
|
|
$id = $this->title->getArticleID();
|
|
|
|
if ( $id ) {
|
2018-08-08 08:29:34 +00:00
|
|
|
return $this->memcached->getWithSetCallback( $this->cachekey, self::CACHE_TTL, function () use ( $id ) {
|
2016-12-14 11:31:25 +00:00
|
|
|
return $this->reparseArticleIfNeeded(
|
2016-12-13 16:41:55 +00:00
|
|
|
json_decode( $this->propsProxy->get( $id, self::INFOBOXES_PROPERTY_NAME ), true )
|
|
|
|
);
|
2015-09-24 14:25:27 +00:00
|
|
|
} );
|
2015-09-22 15:48:38 +00:00
|
|
|
}
|
2015-08-25 12:27:01 +00:00
|
|
|
|
2015-09-22 15:48:38 +00:00
|
|
|
return [ ];
|
|
|
|
}
|
|
|
|
|
2016-12-09 15:55:42 +00:00
|
|
|
/**
|
2016-12-14 11:44:23 +00:00
|
|
|
* If PageProps has an old version of infobox data/metadata then reparse the page and store fresh data
|
|
|
|
* If it doesn't have infoboxes property, we treat it as a page without infoboxes - there might be false negatives
|
2016-12-09 15:55:42 +00:00
|
|
|
*
|
2016-12-13 16:41:55 +00:00
|
|
|
* @param $infoboxes
|
2016-12-09 15:55:42 +00:00
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
2016-12-14 11:31:25 +00:00
|
|
|
protected function reparseArticleIfNeeded( $infoboxes ) {
|
2016-12-14 11:44:23 +00:00
|
|
|
if ( is_array( $infoboxes ) ) {
|
|
|
|
foreach ( $infoboxes as $infobox ) {
|
|
|
|
if (
|
|
|
|
empty( $infobox ) ||
|
2016-12-19 12:26:18 +00:00
|
|
|
!isset( $infobox[ 'parser_tag_version' ] ) ||
|
|
|
|
$infobox[ 'parser_tag_version' ] !== PortableInfoboxParserTagController::PARSER_TAG_VERSION
|
2016-12-14 11:44:23 +00:00
|
|
|
) {
|
|
|
|
$infoboxes = $this->parsingHelper->reparseArticle( $this->title );
|
|
|
|
$this->set( $infoboxes );
|
|
|
|
}
|
2016-12-09 15:55:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-13 16:41:55 +00:00
|
|
|
return $infoboxes;
|
|
|
|
}
|
2016-12-09 15:55:42 +00:00
|
|
|
|
2015-09-22 15:48:38 +00:00
|
|
|
protected function store( $data ) {
|
|
|
|
$id = $this->title->getArticleID();
|
|
|
|
if ( $id ) {
|
2018-08-08 08:29:34 +00:00
|
|
|
$this->memcached->set( $this->cachekey, $data, self::CACHE_TTL );
|
2015-09-23 13:51:41 +00:00
|
|
|
$this->propsProxy->set( $id, [ self::INFOBOXES_PROPERTY_NAME => json_encode( $data ) ] );
|
2015-09-22 15:48:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function clear() {
|
|
|
|
$id = $this->title->getArticleID();
|
|
|
|
if ( $id ) {
|
2015-09-23 13:51:41 +00:00
|
|
|
$this->propsProxy->set( $id, [ self::INFOBOXES_PROPERTY_NAME => '' ] );
|
2015-09-25 15:19:48 +00:00
|
|
|
// don't cache clear state
|
|
|
|
$this->purge();
|
2015-09-22 15:48:38 +00:00
|
|
|
}
|
2015-08-20 13:52:36 +00:00
|
|
|
}
|
2015-06-09 10:14:00 +00:00
|
|
|
}
|