Passed
Push — master ( c0a3a7...3b84a4 )
by Jeroen
58:51
created

engine/classes/Elgg/DeprecationWrapper.php (2 issues)

1
<?php
2
namespace Elgg;
3
/**
4
 * Wrap an object and display warnings whenever the object's variables are
5
 * accessed or a method is used. It can also be used to wrap a string.
6
 *
7
 * Note that the wrapper will not share the type of the wrapped object and will
8
 * fail type hints, instanceof, etc.
9
 *
10
 * This was introduced for deprecating passing particular variables to views
11
 * automatically in elgg_view().
12
 * It can be removed once that use is no longer required.
13
 *
14
 * Wraps:
15
 *  url string in ViewsService
16
 *  config object in ViewsService
17
 *  user object in ViewsService
18
 *  session object in session lib
19
 *  config object in ElggPlugin::includeFile
20
 *
21
 * @access private
22
 *
23
 * @package Elgg.Core
24
 */
25
class DeprecationWrapper implements \ArrayAccess {
26
	/** @var object */
27
	protected $object;
28
29
	/** @var string */
30
	protected $string;
31
32
	/** @var string */
33
	protected $message;
34
35
	/** @var string */
36
	protected $version;
37
38
	/** @var callable */
39
	protected $reporter;
40
41
	/**
42
	 * Create the wrapper
43
	 *
44
	 * @param mixed    $object   The object or string to wrap
45
	 * @param string   $message  The deprecation message to display when used
46
	 * @param string   $version  The Elgg version this was deprecated
47
	 * @param callable $reporter function called to report deprecation
48
	 */
49 4
	public function __construct($object, $message, $version, $reporter = 'elgg_deprecated_notice') {
50 4
		if (is_object($object)) {
51 3
			$this->object = $object;
52
		} else {
53 1
			$this->string = $object;
54
		}
55 4
		$this->message = $message;
56 4
		$this->version = $version;
57 4
		$this->reporter = $reporter;
0 ignored issues
show
Documentation Bug introduced by
It seems like $reporter can also be of type string. However, the property $reporter is declared as type callable. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
58 4
	}
59
60
	/**
61
	 * Get a property on the object
62
	 *
63
	 * @param string $name Property name
64
	 * @return mixed
65
	 */
66 1
	public function __get($name) {
67 1
		$this->displayWarning();
68 1
		return $this->object->$name;
69
	}
70
71
	/**
72
	 * Set a property on the object
73
	 *
74
	 * @param string $name  Property name
75
	 * @param mixed  $value Property value
76
	 * @return void
77
	 */
78
	public function __set($name, $value) {
79
		$this->displayWarning();
80
		$this->object->$name = $value;
81
	}
82
83
	/**
84
	 * Is a property set?
85
	 *
86
	 * @param string $name Property name
87
	 * @return bool
88
	 */
89
	public function __isset($name) {
90
		$this->displayWarning();
91
		return isset($this->object->$name);
92
	}
93
94
	/**
95
	 * Call a method on the object
96
	 *
97
	 * @param string $name      Method name
98
	 * @param array  $arguments Method arguments
99
	 * @return mixed
100
	 */
101 1
	public function __call($name, $arguments) {
102 1
		$this->displayWarning();
103 1
		return call_user_func_array([$this->object, $name], $arguments);
104
	}
105
106
	/**
107
	 * Get the object as string
108
	 *
109
	 * @return string
110
	 */
111 2
	public function __toString() {
112 2
		$this->displayWarning();
113 2
		if (isset($this->string)) {
114 1
			return $this->string;
115
		} else {
116 1
			return (string) $this->object;
117
		}
118
	}
119
120
	/**
121
	 * Display a warning
122
	 *
123
	 * @return void
124
	 */
125 4
	protected function displayWarning() {
126
		// display 3 levels in the function stack to get back to original use
127
		// 1 for __get/__call/__toString()
128
		// 1 for displayWarning()
129
		// 1 for call_user_func()
130 4
		call_user_func($this->reporter, $this->message, $this->version, 3);
131 4
	}
132
133
	/**
134
	 * Array access interface
135
	 *
136
	 * @see \ArrayAccess::offsetSet()
137
	 *
138
	 * @param mixed $key   Name
139
	 * @param mixed $value Value
140
	 *
141
	 * @return void
142
	 */
143 2
	public function offsetSet($key, $value) {
144 2
		$this->displayWarning();
145 2
		if (is_object($this->object) && !$this->object instanceof \ArrayAccess) {
146 1
			$this->object->$key = $value;
147
		} else {
148 1
			if ($key === null) {
149
				// Yes this is necessary. Otherwise $key will be interpreted as empty string
150 1
				$this->object[] = $value;
151
			} else {
152 1
				$this->object[$key] = $value;
153
			}
154
		}
155 2
	}
156
157
	/**
158
	 * Array access interface
159
	 *
160
	 * @see \ArrayAccess::offsetGet()
161
	 *
162
	 * @param mixed $key Name
163
	 *
164
	 * @return mixed
165
	 */
166 2
	public function offsetGet($key) {
167 2
		$this->displayWarning();
168 2
		if (is_object($this->object) && !$this->object instanceof \ArrayAccess) {
169 1
			return $this->object->$key;
170
		} else {
171 1
			return $this->object[$key];
172
		}
173
	}
174
175
	/**
176
	 * Array access interface
177
	 *
178
	 * @see \ArrayAccess::offsetUnset()
179
	 *
180
	 * @param mixed $key Name
181
	 *
182
	 * @return void
183
	 */
184 1
	public function offsetUnset($key) {
185 1
		$this->displayWarning();
186 1
		if (is_object($this->object) && !$this->object instanceof \ArrayAccess) {
187
			unset($this->object->$key);
188
		} else {
189 1
			unset($this->object[$key]);
190
		}
191 1
	}
192
193
	/**
194
	 * Array access interface
195
	 *
196
	 * @see \ArrayAccess::offsetExists()
197
	 *
198
	 * @param mixed $offset Offset
199
	 *
200
	 * @return bool
201
	 */
202
	public function offsetExists($offset) {
203
		$this->displayWarning();
204
		if (is_object($this->object) && !$this->object instanceof \ArrayAccess) {
205
			return isset($this->object->$offset);
206
		} else {
207
			return array_key_exists($offset, $this->object);
0 ignored issues
show
It seems like $this->object can also be of type ArrayAccess; however, parameter $search of array_key_exists() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

207
			return array_key_exists($offset, /** @scrutinizer ignore-type */ $this->object);
Loading history...
208
		}
209
	}
210
}
211
212