jenkins-bot has submitted this change and it was merged. Change subject: Hygiene: fix feedback from scrutinizer #4 ......................................................................
Hygiene: fix feedback from scrutinizer #4 I ran scrutinizer-ci.com against flow - these are some of the things it came up with. https://scrutinizer-ci.com/g/matthiasmullie/mediawiki-extensions-Flow/inspections/d37be56d-1a25-4c3c-8f2e-f47fc8f3c403/issues/ Change-Id: Icecee81bded92f2b74627a0fbd9d0e245aceccd3 --- M includes/Data/Index.php M includes/Data/Mapper/CachingObjectMapper.php M includes/Data/ObjectLocator.php M includes/Data/ObjectStorage.php M includes/Data/Pager/Pager.php M includes/Data/RecentChanges/RecentChanges.php M includes/Data/Storage/BasicDbStorage.php M includes/Data/Storage/BoardHistoryStorage.php M includes/Data/Storage/PostRevisionStorage.php M includes/Data/Storage/RevisionStorage.php M includes/Data/Utils/RawSql.php M includes/Data/Utils/ResultDuplicator.php M includes/Formatter/AbstractFormatter.php M includes/Formatter/Contributions.php M includes/Formatter/ContributionsQuery.php M includes/Formatter/IRCLineUrlFormatter.php M includes/Formatter/RecentChanges.php M includes/Formatter/RecentChangesQuery.php M includes/Formatter/RevisionFormatter.php 19 files changed, 176 insertions(+), 29 deletions(-) Approvals: EBernhardson: Looks good to me, approved jenkins-bot: Verified diff --git a/includes/Data/Index.php b/includes/Data/Index.php index 88ef47e..ebf7f6c 100644 --- a/includes/Data/Index.php +++ b/includes/Data/Index.php @@ -61,7 +61,7 @@ * are broken by evaluating the second term and so on. * * @todo choose a default sort instead of false? - * @return array|false Sorting order of either 'ASC', 'DESC' or false + * @return array|false Columns to sort on */ function getSort(); diff --git a/includes/Data/Mapper/CachingObjectMapper.php b/includes/Data/Mapper/CachingObjectMapper.php index 99a6b96..7abac0c 100644 --- a/includes/Data/Mapper/CachingObjectMapper.php +++ b/includes/Data/Mapper/CachingObjectMapper.php @@ -26,6 +26,11 @@ protected $fromStorageRow; /** + * @var string[] + */ + protected $primaryKey; + + /** * @var MultiDimArray */ protected $loaded; diff --git a/includes/Data/ObjectLocator.php b/includes/Data/ObjectLocator.php index 33b10b9..4f8d8fb 100644 --- a/includes/Data/ObjectLocator.php +++ b/includes/Data/ObjectLocator.php @@ -2,9 +2,10 @@ namespace Flow\Data; -use FormatJson; +use Flow\Exception\FlowException; use Flow\Exception\NoIndexException; use Flow\Model\UUID; +use FormatJson; /** * Denormalized indexes that are query-only. The indexes used here must @@ -33,6 +34,12 @@ */ protected $lifecycleHandlers; + /** + * @param ObjectMapper $mapper + * @param ObjectStorage $storage + * @param Index[] $indexes + * @param LifecycleHandler[] $lifecycleHandlers + */ public function __construct( ObjectMapper $mapper, ObjectStorage $storage, array $indexes = array(), array $lifecycleHandlers = array() ) { $this->mapper = $mapper; $this->storage = $storage; @@ -50,6 +57,10 @@ } public function getIterator() { + if ( !method_exists( $this->storage, 'getIterator' ) ) { + throw new FlowException( 'Storage object of class "' . get_class( $this->storage ) . '" has no iterator.' ); + } + return $this->storage->getIterator(); } @@ -246,7 +257,7 @@ } public function clear() { - // nop, we dont store anything + // nop, we don't store anything } /** diff --git a/includes/Data/ObjectStorage.php b/includes/Data/ObjectStorage.php index 4e02806..fdf6266 100644 --- a/includes/Data/ObjectStorage.php +++ b/includes/Data/ObjectStorage.php @@ -23,7 +23,7 @@ * * @param array $queries list of queries to perform * @param array $options Options to use for all queries - * @return array + * @return array[] Array of results for every query */ function findMulti( array $queries, array $options = array() ); diff --git a/includes/Data/Pager/Pager.php b/includes/Data/Pager/Pager.php index 66c9d13..fddec20 100644 --- a/includes/Data/Pager/Pager.php +++ b/includes/Data/Pager/Pager.php @@ -41,6 +41,11 @@ */ protected $options; + /** + * @var string + */ + protected $offsetKey; + public function __construct( ObjectManager $storage, array $query, array $options ) { // not sure i like this $this->storage = $storage; diff --git a/includes/Data/RecentChanges/RecentChanges.php b/includes/Data/RecentChanges/RecentChanges.php index 03018e6..5954311 100644 --- a/includes/Data/RecentChanges/RecentChanges.php +++ b/includes/Data/RecentChanges/RecentChanges.php @@ -36,6 +36,11 @@ protected $usernames; /** + * @var RecentChangeFactory + */ + protected $rcFactory; + + /** * @param FlowActions $actions * @param UserNameBatch $usernames * @param RecentChangeFactory $rcFactory Creates mw RecentChange instances diff --git a/includes/Data/Storage/BasicDbStorage.php b/includes/Data/Storage/BasicDbStorage.php index 4f13b40..34828c2 100644 --- a/includes/Data/Storage/BasicDbStorage.php +++ b/includes/Data/Storage/BasicDbStorage.php @@ -18,6 +18,22 @@ * Doesn't support auto-increment pk yet */ class BasicDbStorage extends DbStorage { + /** + * @var string + */ + protected $table; + + /** + * @var string[] + */ + protected $primaryKey; + + /** + * @param DbFactory $dbFactory + * @param string $table + * @param string[] $primaryKey + * @throws DataModelException + */ public function __construct( DbFactory $dbFactory, $table, array $primaryKey ) { if ( !$primaryKey ) { throw new DataModelException( 'PK required', 'process-data' ); @@ -69,7 +85,7 @@ $pk = ObjectManager::splitFromRow( $old, $this->primaryKey ); if ( $pk === null ) { $missing = array_diff( $this->primaryKey, array_keys( $old ) ); - throw new DataPersistenceException( 'Row has null primary key: ' . implode( $missing ), 'process-data' ); + throw new DataPersistenceException( 'Row has null primary key: ' . implode( ', ', $missing ), 'process-data' ); } $updates = ObjectManager::calcUpdates( $old, $new ); if ( !$updates ) { @@ -98,7 +114,7 @@ $pk = ObjectManager::splitFromRow( $row, $this->primaryKey ); if ( $pk === null ) { $missing = array_diff( $this->primaryKey, array_keys( $row ) ); - throw new DataPersistenceException( 'Row has null primary key: ' . implode( $missing ), 'process-data' ); + throw new DataPersistenceException( 'Row has null primary key: ' . implode( ', ', $missing ), 'process-data' ); } $pk = $this->preprocessSqlArray( $pk ); diff --git a/includes/Data/Storage/BoardHistoryStorage.php b/includes/Data/Storage/BoardHistoryStorage.php index 2b4219a..19b03dd 100644 --- a/includes/Data/Storage/BoardHistoryStorage.php +++ b/includes/Data/Storage/BoardHistoryStorage.php @@ -26,9 +26,19 @@ $merged = $this->findHeaderHistory( $queries, $options ) + $this->findTopicListHistory( $queries, $options ) + $this->findTopicSummaryHistory( $queries, $options ); - // newest items at the begining of the list + // newest items at the beginning of the list krsort( $merged ); - return RevisionStorage::mergeExternalContent( array( $merged ) ); + + // Merge data from external store & get rid of failures + $res = array( $merged ); + $res = RevisionStorage::mergeExternalContent( $res ); + foreach ( $res as $i => $result ) { + if ( $result ) { + $res[$i] = array_filter( $result, array( $this, 'validate' ) ); + } + } + + return $res; } protected function findHeaderHistory( array $queries, array $options = array() ) { @@ -105,6 +115,17 @@ return $retval; } + /** + * When retrieving revisions from DB, RevisionStorage::mergeExternalContent + * will be called to fetch the content. This could fail, resulting in the + * content being a 'false' value. + * + * {@inheritDoc} + */ + public function validate( array $row ) { + return !isset( $row['rev_content'] ) || $row['rev_content'] !== false; + } + public function getPrimaryKeyColumns() { return array( 'topic_list_id' ); } @@ -124,5 +145,4 @@ public function getIterator() { throw new DataModelException( 'Not Implemented', 'process-data' ); } - } diff --git a/includes/Data/Storage/PostRevisionStorage.php b/includes/Data/Storage/PostRevisionStorage.php index a156d30..5d12b2c 100644 --- a/includes/Data/Storage/PostRevisionStorage.php +++ b/includes/Data/Storage/PostRevisionStorage.php @@ -11,7 +11,17 @@ * SQL storage and query for PostRevision instances */ class PostRevisionStorage extends RevisionStorage { + /** + * @var TreeRepository + */ + protected $treeRepo; + /** + * @param DbFactory $dbFactory + * @param array|false List of external store servers available for insert + * or false to disable. See $wgFlowExternalStore. + * @param TreeRepository $treeRepo + */ public function __construct( DbFactory $dbFactory, $externalStore, TreeRepository $treeRepo ) { parent::__construct( $dbFactory, $externalStore ); $this->treeRepo = $treeRepo; @@ -60,7 +70,7 @@ } if ( !$res ) { - return false; + return array(); } return $rows; @@ -98,15 +108,15 @@ ); if ( !$res ) { - return false; + return array(); } return $changes; } - // this doesnt delete the whole post, it just deletes the revision. + // this doesn't delete the whole post, it just deletes the revision. // The post will *always* exist in the tree structure, its just a tree - // and we arn't going to re-parent its children; + // and we aren't going to re-parent its children; protected function removeRelated( array $row ) { return $this->dbFactory->getDB( DB_MASTER )->delete( $this->joinTable(), diff --git a/includes/Data/Storage/RevisionStorage.php b/includes/Data/Storage/RevisionStorage.php index 7d5a142..fb6dfda 100644 --- a/includes/Data/Storage/RevisionStorage.php +++ b/includes/Data/Storage/RevisionStorage.php @@ -97,7 +97,7 @@ /** * @param DbFactory $dbFactory - * @param array|false List of externel store servers available for insert + * @param array|false List of external store servers available for insert * or false to disable. See $wgFlowExternalStore. */ public function __construct( DbFactory $dbFactory, $externalStore ) { @@ -459,7 +459,7 @@ return false; } } - return $this->updateRelated( $changeSet, $old ); + return (bool) $this->updateRelated( $changeSet, $old ); } diff --git a/includes/Data/Utils/RawSql.php b/includes/Data/Utils/RawSql.php index 0cd9af9..5b6a2af 100644 --- a/includes/Data/Utils/RawSql.php +++ b/includes/Data/Utils/RawSql.php @@ -8,11 +8,13 @@ * plain strings. */ class RawSql { - function __construct( $sql ) { + protected $sql; + + public function __construct( $sql ) { $this->sql = $sql; } - function getSQL( $db ) { + public function getSQL( $db ) { if ( is_callable( $this->sql ) ) { return call_user_func( $this->sql, $db ); } diff --git a/includes/Data/Utils/ResultDuplicator.php b/includes/Data/Utils/ResultDuplicator.php index b8121d3..6f3bacf 100644 --- a/includes/Data/Utils/ResultDuplicator.php +++ b/includes/Data/Utils/ResultDuplicator.php @@ -13,13 +13,39 @@ // // Maintains merge ordering class ResultDuplicator { - // Maps from the query array to its position in the query array + /** + * Maps from the query array to its position in the query array + * + * @var array + */ protected $queryKeys; + + /** + * @var int + */ + protected $dimensions; + + /** + * @var MultiDimArray + */ + protected $desiredOrder; + + /** + * @var MultiDimArray + */ protected $queryMap; - protected $queries = array(); + + /** + * @var MultiDimArray + */ protected $result; /** + * @var array + */ + protected $queries = array(); + + /** * @param array $queryKeys * @param int $dimensions */ diff --git a/includes/Formatter/AbstractFormatter.php b/includes/Formatter/AbstractFormatter.php index ba6abcd..8d74217 100644 --- a/includes/Formatter/AbstractFormatter.php +++ b/includes/Formatter/AbstractFormatter.php @@ -107,7 +107,7 @@ $request = array_keys( $links ); } elseif ( !$request ) { // empty array was passed - return array(); + return ''; } $have = array_combine( $request, $request ); diff --git a/includes/Formatter/Contributions.php b/includes/Formatter/Contributions.php index 2dddbe7..7fe3866 100644 --- a/includes/Formatter/Contributions.php +++ b/includes/Formatter/Contributions.php @@ -42,6 +42,9 @@ protected function formatHtml( FormatterRow $row, IContextSource $ctx ) { $this->serializer->setIncludeHistoryProperties( true ); $data = $this->serializer->formatApi( $row, $ctx ); + if ( !$data ) { + throw new FlowException( 'Could not format data for row ' . $row->revision->getRevisionId()->getAlphadecimal() ); + } $charDiff = ChangesList::showCharacterDifference( $data['size']['old'], diff --git a/includes/Formatter/ContributionsQuery.php b/includes/Formatter/ContributionsQuery.php index 9e8431b..c3d9c70 100644 --- a/includes/Formatter/ContributionsQuery.php +++ b/includes/Formatter/ContributionsQuery.php @@ -22,7 +22,7 @@ protected $cache; /** - * @var DBFactory + * @var DbFactory */ protected $dbFactory; @@ -89,6 +89,8 @@ * @return array Query conditions */ protected function buildConditions( ContribsPager $pager, $offset, $descending ) { + $conditions = array(); + // Work out user condition if ( $pager->contribs == 'newbie' ) { list( $minUserId, $excludeUserIds ) = $this->getNewbieConditionInfo( $pager ); @@ -234,8 +236,14 @@ } // get content in external storage - $revisions = RevisionStorage::mergeExternalContent( array( $revisions ) ); - $revisions = reset( $revisions ); + $res = array( $revisions ); + $res = RevisionStorage::mergeExternalContent( $res ); + foreach ( $res as $i => $result ) { + if ( $result ) { + $res[$i] = array_filter( $result, array( $this, 'validate' ) ); + } + } + $revisions = reset( $res ); // we have all required data to build revision $mapper = $this->storage->getStorage( $revisionClass )->getMapper(); @@ -291,6 +299,17 @@ return array( $minUserId, $excludeUserIds ); } + + /** + * When retrieving revisions from DB, self::mergeExternalContent will be + * called to fetch the content. This could fail, resulting in the content + * being a 'false' value. + * + * {@inheritDoc} + */ + public function validate( array $row ) { + return !isset( $row['rev_content'] ) || $row['rev_content'] !== false; + } } class ContributionsRow extends FormatterRow { diff --git a/includes/Formatter/IRCLineUrlFormatter.php b/includes/Formatter/IRCLineUrlFormatter.php index d81c7b1..6fe8058 100644 --- a/includes/Formatter/IRCLineUrlFormatter.php +++ b/includes/Formatter/IRCLineUrlFormatter.php @@ -23,13 +23,18 @@ public function getLine( array $feed, RecentChange $rc, $actionComment ) { /** @var RecentChangesQuery $query */ $query = Container::get( 'query.recentchanges' ); - //throw new \Exception($rc->mAttribs['rc_params']); $query->loadMetadataBatch( array( (object)$rc->mAttribs ) ); $rcRow = $query->getResult( null, $rc ); + $ctx = \RequestContext::getMain(); $data = $this->serializer->formatApi( $rcRow, $ctx ); + if ( !$data ) { + throw new FlowException( 'Could not format data for row ' . $rcRow->revision->getRevisionId()->getAlphadecimal() ); + } $this->serializer->setIncludeHistoryProperties( true ); + $rc->mAttribs['rc_comment'] = $this->formatDescription( $data, $ctx ); + /** @var RCFeedFormatter $formatter */ $formatter = new $feed['original_formatter'](); return $formatter->getLine( $feed, $rc, $actionComment ); diff --git a/includes/Formatter/RecentChanges.php b/includes/Formatter/RecentChanges.php index 73a5107..853471e 100644 --- a/includes/Formatter/RecentChanges.php +++ b/includes/Formatter/RecentChanges.php @@ -29,6 +29,10 @@ $this->serializer->setIncludeHistoryProperties( true ); $data = $this->serializer->formatApi( $row, $ctx ); + if ( !$data ) { + throw new FlowException( 'Could not format data for row ' . $row->revision->getRevisionId()->getAlphadecimal() ); + } + // @todo where should this go? $data['size'] = array( 'old' => $row->recentChange->getAttribute( 'rc_old_len' ), diff --git a/includes/Formatter/RecentChangesQuery.php b/includes/Formatter/RecentChangesQuery.php index b3dc4d1..1edc7f4 100644 --- a/includes/Formatter/RecentChangesQuery.php +++ b/includes/Formatter/RecentChangesQuery.php @@ -3,7 +3,7 @@ namespace Flow\Formatter; use Flow\Data\ManagerGroup; -use Flow\Data\RecentChanges\RecentChanges; +use Flow\Data\RecentChanges\RecentChanges as RecentChangesHandler; // not to be confused with RecentChanges in Flow\Formatter namespace use Flow\Exception\FlowException; use Flow\FlowActions; use Flow\Model\UUID; @@ -48,7 +48,7 @@ public function loadMetadataBatch( $rows, $isWatchlist = false ) { $needed = array(); foreach ( $rows as $row ) { - if ( !isset( $row->rc_source ) || $row->rc_source !== RecentChanges::SRC_FLOW ) { + if ( !isset( $row->rc_source ) || $row->rc_source !== RecentChangesHandler::SRC_FLOW ) { continue; } if ( !isset( $row->rc_params ) ) { diff --git a/includes/Formatter/RevisionFormatter.php b/includes/Formatter/RevisionFormatter.php index 79371f0..6ac5217 100644 --- a/includes/Formatter/RevisionFormatter.php +++ b/includes/Formatter/RevisionFormatter.php @@ -89,6 +89,16 @@ protected $userLinks = array(); /** + * @var UserNameBatch + */ + protected $usernames; + + /** + * @var GenderCache + */ + protected $genderCache; + + /** * @param RevisionActionPermissions $permissions * @param Templating $templating * @param UserNameBatch $usernames @@ -421,8 +431,11 @@ if ( // thanks extension must be available !class_exists( 'ThanksHooks' ) || - // anon's can't thank + // anons can't thank $user->isAnon() || + // can only thank for PostRevisions + // (other revision objects have mo getCreator* methods) + !$revision instanceof PostRevision || // can't thank an anon user $revision->getCreatorIp() || // can't thank self @@ -436,11 +449,13 @@ case 'reply': if ( !$postId ) { throw new FlowException( "$type called without \$postId" ); + } elseif ( !$revision instanceof PostRevision ) { + throw new FlowException( "$type called without PostRevision object" ); } /* - * If the post being replied is at or exceeds the max threading - * depth, the reply link should point to parent. + * If the post being replied to is at or exceeds the max + * threading depth, the reply link should point to parent. */ $replyToId = $postId; $replyToRevision = $revision; @@ -866,6 +881,7 @@ if ( !$revision instanceof PostSummary ) { throw new FlowException( 'Expected PostSummary but received ' . get_class( $revision ) ); } + /** @var PostRevision $post */ $post = $revision->getCollection()->getPost()->getLastRevision(); if ( $post->isTopicTitle() ) { return Message::plaintextParam( $this->templating->getContent( $post, 'wikitext' ) ); -- To view, visit https://gerrit.wikimedia.org/r/167785 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: Icecee81bded92f2b74627a0fbd9d0e245aceccd3 Gerrit-PatchSet: 2 Gerrit-Project: mediawiki/extensions/Flow Gerrit-Branch: master Gerrit-Owner: Matthias Mullie <mmul...@wikimedia.org> Gerrit-Reviewer: EBernhardson <ebernhard...@wikimedia.org> Gerrit-Reviewer: SG <shah...@gmail.com> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits