Completed
Push — master ( 43acf6...5d1976 )
by mw
12s
created

NewRevisionFromEditComplete::doProcess()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 42
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 42
ccs 21
cts 21
cp 1
rs 8.8571
cc 2
eloc 24
nc 2
nop 0
crap 2
1
<?php
2
3
namespace SMW\MediaWiki\Hooks;
4
5
use ParserOutput;
6
use SMW\ApplicationFactory;
7
use SMW\EventHandler;
8
use SMW\MediaWiki\EditInfoProvider;
9
10
/**
11
 * Hook: NewRevisionFromEditComplete called when a revision was inserted
12
 * due to an edit
13
 *
14
 * Fetch additional information that is related to the saving that has just happened,
15
 * e.g. regarding the last edit date. In runs where this hook is not triggered, the
16
 * last DB entry (of MW) will be used to fill such properties.
17
 *
18
 * Called from LocalFile.php, SpecialImport.php, Article.php, Title.php
19
 *
20
 * @see https://www.mediawiki.org/wiki/Manual:Hooks/NewRevisionFromEditComplete
21
 *
22
 * @license GNU GPL v2+
23
 * @since 1.9
24
 *
25
 * @author mwjames
26
 */
27
class NewRevisionFromEditComplete {
28
29
	/**
30
	 * @var WikiPage
31
	 */
32
	private $wikiPage = null;
33
34
	/**
35
	 * @var Revision
36
	 */
37
	private $revision = null;
38
39
	/**
40
	 * @var integer
41
	 */
42
	private $baseId = null;
43
44
	/**
45
	 * @var User|null
46
	 */
47
	private $user = null;
48
49
	/**
50
	 * @var ParserOutput|null
51
	 */
52
	private $parserOutput = null;
53
54
	/**
55
	 * @since  1.9
56
	 *
57
	 * @param WikiPage $article the article edited
0 ignored issues
show
Bug introduced by
There is no parameter named $article. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
58
	 * @param Revision $rev the new revision. Revision object
0 ignored issues
show
Bug introduced by
There is no parameter named $rev. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
59
	 * @param $baseId the revision ID this was based off, if any
60
	 * @param User $user the revision author. User object
61
	 */
62 152
	public function __construct( $wikiPage, $revision, $baseId, $user = null ) {
63 152
		$this->wikiPage = $wikiPage;
64 152
		$this->revision = $revision;
65 152
		$this->baseId = $baseId;
66 152
		$this->user = $user;
67 152
	}
68
69
	/**
70
	 * @since 1.9
71
	 *
72
	 * @return boolean
73
	 */
74 151
	public function process() {
75 151
		return $this->canUseParserOutputFromEditInfo() ? $this->doProcess() : true;
76
	}
77
78 151
	private function canUseParserOutputFromEditInfo() {
79
80 151
		$editInfoProvider = new EditInfoProvider(
81 151
			$this->wikiPage,
82 151
			$this->revision,
83 151
			$this->user
84
		);
85
86 151
		$this->parserOutput = $editInfoProvider->fetchEditInfo()->getOutput();
0 ignored issues
show
Documentation Bug introduced by
It seems like $editInfoProvider->fetchEditInfo()->getOutput() of type object<SMW\MediaWiki\ParserOutput> is incompatible with the declared type object<ParserOutput>|null of property $parserOutput.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
87
88 151
		return $this->parserOutput instanceof ParserOutput;
0 ignored issues
show
Bug introduced by
The class ParserOutput does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
89
	}
90
91 150
	private function doProcess() {
92
93 150
		$applicationFactory = ApplicationFactory::getInstance();
94 150
		$title = $this->wikiPage->getTitle();
95
96 150
		$parserData = $applicationFactory->newParserData(
97
			$title,
98 150
			$this->parserOutput
0 ignored issues
show
Bug introduced by
It seems like $this->parserOutput can be null; however, newParserData() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
99
		);
100
101 150
		$pageInfoProvider = $applicationFactory->newMwCollaboratorFactory()->newPageInfoProvider(
102 150
			$this->wikiPage,
103 150
			$this->revision,
104 150
			$this->user
105
		);
106
107 150
		$propertyAnnotatorFactory = $applicationFactory->newPropertyAnnotatorFactory();
108
109 150
		$propertyAnnotator = $propertyAnnotatorFactory->newPredefinedPropertyAnnotator(
110 150
			$parserData->getSemanticData(),
111
			$pageInfoProvider
112
		);
113
114 150
		$propertyAnnotator->addAnnotation();
115
116 150
		$parserData->pushSemanticDataToParserOutput();
117
118 150
		$dispatchContext = EventHandler::getInstance()->newDispatchContext();
119 150
		$dispatchContext->set( 'title', $this->wikiPage->getTitle() );
120
121 150
		EventHandler::getInstance()->getEventDispatcher()->dispatch(
122 150
			'cached.propertyvalues.prefetcher.reset',
123
			$dispatchContext
124
		);
125
126
		// If the concept was altered make sure to delete the cache
127 150
		if ( $title->getNamespace() === SMW_NS_CONCEPT ) {
128 5
			$applicationFactory->getStore()->deleteConceptCache( $title );
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class SMW\Store as the method deleteConceptCache() does only exist in the following sub-classes of SMW\Store: SMWSQLStore3, SMWSparqlStore, SMW\SPARQLStore\SPARQLStore, SMW\Tests\Utils\Mock\FakeQueryStore. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
129
		}
130
131 150
		return true;
132
	}
133
134
}
135