Completed
Push — master ( 5ebf6f...02d949 )
by mw
12s
created

DependencyLinksTableUpdater::addToUpdateList()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 6
nc 3
nop 2
dl 0
loc 12
ccs 7
cts 7
cp 1
crap 5
rs 8.8571
c 1
b 0
f 0
1
<?php
2
3
namespace SMW\SQLStore\QueryDependency;
4
5
use SMW\DIWikiPage;
6
use SMW\SQLStore\SQLStore;
7
use SMW\Store;
8
9
/**
10
 * @license GNU GPL v2+
11
 * @since 2.4
12
 *
13
 * @author mwjames
14
 */
15
class DependencyLinksTableUpdater {
16
17
	/**
18
	 * @var array
19
	 */
20
	private static $updateList = array();
21
22
	/**
23
	 * @var Store
24
	 */
25
	private $store = null;
26
27
	/**
28
	 * @var Database
29
	 */
30
	private $connection = null;
31
32
	/**
33
	 * @since 2.4
34
	 *
35
	 * @param Store $store
36
	 */
37 165
	public function __construct( Store $store ) {
38 165
		$this->store = $store;
39 165
		$this->connection = $this->store->getConnection( 'mw.db' );
40 165
	}
41
42
	/**
43
	 * @since 2.4
44
	 *
45
	 * @return Store
46
	 */
47 160
	public function getStore() {
48 160
		return $this->store;
49
	}
50
51
	/**
52
	 * @since 2.4
53
	 */
54 2
	public function clear() {
55 2
		self::$updateList = array();
56 2
	}
57
58
	/**
59
	 * @since 2.4
60
	 *
61
	 * @param integer $sid
62
	 * @param array|null $dependencyList
63
	 */
64 55
	public function addToUpdateList( $sid, array $dependencyList = null ) {
65
66 55
		if ( $sid == 0 || $dependencyList === null || $dependencyList === array() ) {
67 53
			return null;
68
		}
69
70 53
		if ( !isset( self::$updateList[$sid] ) ) {
71 53
			return self::$updateList[$sid] = $dependencyList;
72
		}
73
74 51
		self::$updateList[$sid] = array_merge( self::$updateList[$sid], $dependencyList );
75 51
	}
76
77
	/**
78
	 * @since 2.4
79
	 */
80 53
	public function doUpdate() {
81 53
		foreach ( self::$updateList as $sid => $dependencyList ) {
82
83 53
			if ( $dependencyList === array() ) {
84 51
				continue;
85
			}
86
87 53
			$this->updateDependencyList( $sid, $dependencyList );
88 53
			self::$updateList[$sid] = array();
89
		}
90 53
	}
91
92
	/**
93
	 * @since 2.4
94
	 *
95
	 * @param array $dependencyList
0 ignored issues
show
Bug introduced by
There is no parameter named $dependencyList. 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...
96
	 */
97 54
	public function deleteDependenciesFromList( array $deleteIdList ) {
98
99 54
		wfDebugLog( 'smw', __METHOD__ . ' ' . implode( ' ,', $deleteIdList ) . "\n" );
100
101 54
		$this->connection->beginAtomicTransaction( __METHOD__ );
102
103 54
		$this->connection->delete(
104 54
			SQLStore::QUERY_LINKS_TABLE,
105
			array(
106 54
				's_id' => $deleteIdList
107
			),
108 54
			__METHOD__
109
		);
110
111 54
		$this->connection->endAtomicTransaction( __METHOD__ );
112 54
	}
113
114
	/**
115
	 * @since 2.4
116
	 *
117
	 * @param integer $sid
118
	 * @param array $dependencyList
119
	 */
120 53
	public function updateDependencyList( $sid, array $dependencyList ) {
121
122 53
		$this->connection->beginAtomicTransaction( __METHOD__ );
123
124
		// Before an insert, delete all entries that for the criteria which is
125
		// cheaper then doing an individual upsert or selectRow, this also ensures
126
		// that entries are self-corrected for dependencies matched
127 53
		$this->connection->delete(
128 53
			SQLStore::QUERY_LINKS_TABLE,
129
			array(
130 53
				's_id' => $sid
131
			),
132 53
			__METHOD__
133
		);
134
135 53
		if ( $sid == 0 ) {
136
			return $this->connection->endAtomicTransaction( __METHOD__ );
137
		}
138
139 53
		$inserts = array();
140
141 53
		foreach ( $dependencyList as $dependency ) {
142
143 53
			if ( !$dependency instanceof DIWikiPage ) {
144
				continue;
145
			}
146
147 53
			$oid = $this->getIdForSubject( $dependency );
148
149
			// If the ID_TABLE didn't contained an valid ID then we create one ourselves
150
			// to ensure that object entities are tracked from the start
151
			// This can happen when a query is added with object reference that have not
152
			// yet been referenced as annotation and therefore do not recognized as
153
			// value annotation
154 53
			if ( $oid < 1 && ( ( $oid = $this->tryToMakeIdForSubject( $dependency ) ) < 1 ) ) {
155 10
				continue;
156
			}
157
158 53
			$inserts[$sid . $oid] = array(
159 53
				's_id' => $sid,
160 53
				'o_id' => $oid
161
			);
162
		}
163
164 53
		if ( $inserts === array() ) {
165
			return $this->connection->endAtomicTransaction( __METHOD__ );
166
		}
167
168
		// MW's multi-array insert needs a numeric dimensional array but the key
169
		// was used with a hash to avoid duplicate entries hence the re-copy
170 53
		$inserts = array_values( $inserts );
171
172 53
		wfDebugLog( 'smw', __METHOD__ . ' insert for SID ' . $sid . "\n" );
173
174 53
		$this->connection->insert(
175 53
			SQLStore::QUERY_LINKS_TABLE,
176
			$inserts,
177 53
			__METHOD__
178
		);
179
180 53
		$this->connection->endAtomicTransaction( __METHOD__ );
181 53
	}
182
183
	/**
184
	 * @since 2.4
185
	 *
186
	 * @param DIWikiPage $subject, $subobjectName
0 ignored issues
show
Documentation introduced by
There is no parameter named $subject,. Did you maybe mean $subject?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

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

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

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

Loading history...
187
	 * @param string $subobjectName
188
	 */
189 53
	public function getIdForSubject( DIWikiPage $subject, $subobjectName = '' ) {
0 ignored issues
show
Unused Code introduced by
The parameter $subobjectName is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
190
191 53
		$id = $this->store->getObjectIds()->getIDFor(
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 getObjectIds() 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...
192
			$subject
193
		);
194
195 53
		return $id;
196
	}
197
198
	/**
199
	 * @since 2.4
200
	 *
201
	 * @param DIWikiPage $subject, $subobjectName
0 ignored issues
show
Documentation introduced by
There is no parameter named $subject,. Did you maybe mean $subject?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

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

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

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

Loading history...
202
	 * @param string $subobjectName
203
	 */
204 12
	public function tryToMakeIdForSubject( DIWikiPage $subject, $subobjectName = '' ) {
205
206 12
		if ( $subject->getNamespace() !== NS_CATEGORY && $subject->getNamespace() !== SMW_NS_PROPERTY ) {
207 10
			return 0;
208
		}
209
210 3
		$id = $this->store->getObjectIds()->makeSMWPageID(
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 getObjectIds() 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...
211 3
			$subject->getDBkey(),
212 3
			$subject->getNamespace(),
213 3
			$subject->getInterwiki(),
214
			$subobjectName,
215 3
			false
216
		);
217
218 3
		wfDebugLog( 'smw', __METHOD__ . " add new {$id} ID for " . $subject->getHash() . " \n" );
219
220 3
		return $id;
221
	}
222
223
}
224