123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315 |
- <?php
- /**
- * Access to properties of a page.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
- use Wikimedia\ScopedCallback;
- /**
- * Gives access to properties of a page.
- *
- * @since 1.27
- */
- class PageProps {
- /**
- * @var PageProps
- */
- private static $instance;
- /**
- * Overrides the default instance of this class
- * This is intended for use while testing and will fail if MW_PHPUNIT_TEST is not defined.
- *
- * If this method is used it MUST also be called with null after a test to ensure a new
- * default instance is created next time getInstance is called.
- *
- * @since 1.27
- *
- * @param PageProps|null $store
- *
- * @return ScopedCallback to reset the overridden value
- * @throws MWException
- */
- public static function overrideInstance( PageProps $store = null ) {
- if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
- throw new MWException(
- 'Cannot override ' . __CLASS__ . 'default instance in operation.'
- );
- }
- $previousValue = self::$instance;
- self::$instance = $store;
- return new ScopedCallback( function () use ( $previousValue ) {
- self::$instance = $previousValue;
- } );
- }
- /**
- * @return PageProps
- */
- public static function getInstance() {
- if ( self::$instance === null ) {
- self::$instance = new self();
- }
- return self::$instance;
- }
- /** Cache parameters */
- const CACHE_TTL = 10; // integer; TTL in seconds
- const CACHE_SIZE = 100; // integer; max cached pages
- /** Property cache */
- private $cache = null;
- /**
- * Create a PageProps object
- */
- private function __construct() {
- $this->cache = new MapCacheLRU( self::CACHE_SIZE );
- }
- /**
- * Ensure that cache has at least this size
- * @param int $size
- */
- public function ensureCacheSize( $size ) {
- if ( $this->cache->getMaxSize() < $size ) {
- $this->cache->setMaxSize( $size );
- }
- }
- /**
- * Given one or more Titles and one or more names of properties,
- * returns an associative array mapping page ID to property value.
- * Pages in the provided set of Titles that do not have a value for
- * the given properties will not appear in the returned array. If a
- * single Title is provided, it does not need to be passed in an array,
- * but an array will always be returned. If a single property name is
- * provided, it does not need to be passed in an array. In that case,
- * an associative array mapping page ID to property value will be
- * returned; otherwise, an associative array mapping page ID to
- * an associative array mapping property name to property value will be
- * returned. An empty array will be returned if no matching properties
- * were found.
- *
- * @param Title[]|Title $titles
- * @param string[]|string $propertyNames
- * @return array associative array mapping page ID to property value
- */
- public function getProperties( $titles, $propertyNames ) {
- if ( is_array( $propertyNames ) ) {
- $gotArray = true;
- } else {
- $propertyNames = [ $propertyNames ];
- $gotArray = false;
- }
- $values = [];
- $goodIDs = $this->getGoodIDs( $titles );
- $queryIDs = [];
- foreach ( $goodIDs as $pageID ) {
- foreach ( $propertyNames as $propertyName ) {
- $propertyValue = $this->getCachedProperty( $pageID, $propertyName );
- if ( $propertyValue === false ) {
- $queryIDs[] = $pageID;
- break;
- } elseif ( $gotArray ) {
- $values[$pageID][$propertyName] = $propertyValue;
- } else {
- $values[$pageID] = $propertyValue;
- }
- }
- }
- if ( $queryIDs ) {
- $dbr = wfGetDB( DB_REPLICA );
- $result = $dbr->select(
- 'page_props',
- [
- 'pp_page',
- 'pp_propname',
- 'pp_value'
- ],
- [
- 'pp_page' => $queryIDs,
- 'pp_propname' => $propertyNames
- ],
- __METHOD__
- );
- foreach ( $result as $row ) {
- $pageID = $row->pp_page;
- $propertyName = $row->pp_propname;
- $propertyValue = $row->pp_value;
- $this->cacheProperty( $pageID, $propertyName, $propertyValue );
- if ( $gotArray ) {
- $values[$pageID][$propertyName] = $propertyValue;
- } else {
- $values[$pageID] = $propertyValue;
- }
- }
- }
- return $values;
- }
- /**
- * Get all page property values.
- * Given one or more Titles, returns an associative array mapping page
- * ID to an associative array mapping property names to property
- * values. Pages in the provided set of Titles that do not have any
- * properties will not appear in the returned array. If a single Title
- * is provided, it does not need to be passed in an array, but an array
- * will always be returned. An empty array will be returned if no
- * matching properties were found.
- *
- * @param Title[]|Title $titles
- * @return array associative array mapping page ID to property value array
- */
- public function getAllProperties( $titles ) {
- $values = [];
- $goodIDs = $this->getGoodIDs( $titles );
- $queryIDs = [];
- foreach ( $goodIDs as $pageID ) {
- $pageProperties = $this->getCachedProperties( $pageID );
- if ( $pageProperties === false ) {
- $queryIDs[] = $pageID;
- } else {
- $values[$pageID] = $pageProperties;
- }
- }
- if ( $queryIDs != [] ) {
- $dbr = wfGetDB( DB_REPLICA );
- $result = $dbr->select(
- 'page_props',
- [
- 'pp_page',
- 'pp_propname',
- 'pp_value'
- ],
- [
- 'pp_page' => $queryIDs,
- ],
- __METHOD__
- );
- $currentPageID = 0;
- $pageProperties = [];
- foreach ( $result as $row ) {
- $pageID = $row->pp_page;
- if ( $currentPageID != $pageID ) {
- if ( $pageProperties != [] ) {
- $this->cacheProperties( $currentPageID, $pageProperties );
- $values[$currentPageID] = $pageProperties;
- }
- $currentPageID = $pageID;
- $pageProperties = [];
- }
- $pageProperties[$row->pp_propname] = $row->pp_value;
- }
- if ( $pageProperties != [] ) {
- $this->cacheProperties( $pageID, $pageProperties );
- $values[$pageID] = $pageProperties;
- }
- }
- return $values;
- }
- /**
- * @param Title[]|Title $titles
- * @return array array of good page IDs
- */
- private function getGoodIDs( $titles ) {
- $result = [];
- if ( is_array( $titles ) ) {
- ( new LinkBatch( $titles ) )->execute();
- foreach ( $titles as $title ) {
- $pageID = $title->getArticleID();
- if ( $pageID > 0 ) {
- $result[] = $pageID;
- }
- }
- } else {
- $pageID = $titles->getArticleID();
- if ( $pageID > 0 ) {
- $result[] = $pageID;
- }
- }
- return $result;
- }
- /**
- * Get a property from the cache.
- *
- * @param int $pageID page ID of page being queried
- * @param string $propertyName name of property being queried
- * @return string|bool property value array or false if not found
- */
- private function getCachedProperty( $pageID, $propertyName ) {
- if ( $this->cache->hasField( $pageID, $propertyName, self::CACHE_TTL ) ) {
- return $this->cache->getField( $pageID, $propertyName );
- }
- if ( $this->cache->hasField( 0, $pageID, self::CACHE_TTL ) ) {
- $pageProperties = $this->cache->getField( 0, $pageID );
- if ( isset( $pageProperties[$propertyName] ) ) {
- return $pageProperties[$propertyName];
- }
- }
- return false;
- }
- /**
- * Get properties from the cache.
- *
- * @param int $pageID page ID of page being queried
- * @return string|bool property value array or false if not found
- */
- private function getCachedProperties( $pageID ) {
- if ( $this->cache->hasField( 0, $pageID, self::CACHE_TTL ) ) {
- return $this->cache->getField( 0, $pageID );
- }
- return false;
- }
- /**
- * Save a property to the cache.
- *
- * @param int $pageID page ID of page being cached
- * @param string $propertyName name of property being cached
- * @param mixed $propertyValue value of property
- */
- private function cacheProperty( $pageID, $propertyName, $propertyValue ) {
- $this->cache->setField( $pageID, $propertyName, $propertyValue );
- }
- /**
- * Save properties to the cache.
- *
- * @param int $pageID page ID of page being cached
- * @param string[] $pageProperties associative array of page properties to be cached
- */
- private function cacheProperties( $pageID, $pageProperties ) {
- $this->cache->clear( $pageID );
- $this->cache->setField( 0, $pageID, $pageProperties );
- }
- }
|