Completed
Pull Request — master (#5247)
by Damian
11:17
created

ChangeSetTest   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 338
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 2
Bugs 0 Features 1
Metric Value
wmc 21
c 2
b 0
f 1
lcom 1
cbo 10
dl 0
loc 338
rs 10

12 Methods

Rating   Name   Duplication   Size   Complexity  
A publishAllFixtures() 0 10 3
C assertChangeSetLooksLike() 0 29 8
A testRepeatedSyncIsNOP() 0 19 1
B testSync() 0 42 1
B testIsSynced() 0 28 1
B testCanPublish() 0 27 1
A testCanRevert() 0 3 1
A testCanEdit() 0 18 1
A testCanCreate() 0 9 1
A testCanDelete() 0 18 1
A testCanView() 0 18 1
B testPublish() 0 81 1
1
<?php
2
3
/**
4
 * Provides a set of targettable permissions for tested models
5
 *
6
 * @mixin Versioned
7
 * @mixin DataObject
8
 */
9
trait ChangeSetTest_Permissions {
10
	public function canEdit($member = null) {
11
		return $this->can(__FUNCTION__, $member);
12
	}
13
14
	public function canDelete($member = null) {
15
		return $this->can(__FUNCTION__, $member);
16
	}
17
18
	public function canCreate($member = null, $context = array()) {
19
		return $this->can(__FUNCTION__, $member, $context);
20
	}
21
22
	public function canPublish($member = null, $context = array()) {
23
		return $this->can(__FUNCTION__, $member, $context);
24
	}
25
26
	public function canUnpublish($member = null, $context = array()) {
27
		return $this->can(__FUNCTION__, $member, $context);
28
	}
29
30
	public function can($perm, $member = null, $context = array()) {
0 ignored issues
show
Unused Code introduced by
The parameter $context 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...
31
		$perms = [
32
			"PERM_{$perm}",
33
			'CAN_ALL',
34
		];
35
		return Permission::checkMember($member, $perms);
36
	}
37
}
38
39
/**
40
 * @mixin Versioned
41
 */
42
class ChangeSetTest_Base extends DataObject implements TestOnly {
43
	use ChangeSetTest_Permissions;
44
45
	private static $db = [
46
		'Foo' => 'Int',
47
	];
48
49
	private static $has_many = [
50
		'Mids' => 'ChangeSetTest_Mid',
51
	];
52
53
	private static $owns = [
54
		'Mids',
55
	];
56
57
	private static $extensions = [
58
		"Versioned",
59
	];
60
}
61
62
/**
63
 * @mixin Versioned
64
 */
65
class ChangeSetTest_Mid extends DataObject implements TestOnly {
66
	use ChangeSetTest_Permissions;
67
68
	private static $db = [
69
		'Bar' => 'Int',
70
	];
71
72
	private static $has_one = [
73
		'Base' => 'ChangeSetTest_Base',
74
		'End' => 'ChangeSetTest_End',
75
	];
76
77
	private static $owns = [
78
		'End',
79
	];
80
81
	private static $extensions = [
82
		"Versioned",
83
	];
84
}
85
86
/**
87
 * @mixin Versioned
88
 */
89
class ChangeSetTest_End extends DataObject implements TestOnly {
90
	use ChangeSetTest_Permissions;
91
92
	private static $db = [
93
		'Baz' => 'Int',
94
	];
95
96
	private static $extensions = [
97
		"Versioned",
98
	];
99
}
100
101
/**
102
 * Test {@see ChangeSet} and {@see ChangeSetItem} models
103
 */
104
class ChangeSetTest extends SapphireTest {
105
106
	protected static $fixture_file = 'ChangeSetTest.yml';
107
108
	protected $extraDataObjects = [
109
		'ChangeSetTest_Base',
110
		'ChangeSetTest_Mid',
111
		'ChangeSetTest_End',
112
	];
113
114
	/**
115
	 * Automatically publish all objects
116
	 */
117
	protected function publishAllFixtures() {
118
		$this->logInWithPermission('ADMIN');
119
		foreach($this->fixtureFactory->getFixtures() as $class => $fixtures) {
120
			foreach ($fixtures as $handle => $id) {
121
				/** @var Versioned|DataObject $object */
122
				$object = $this->objFromFixture($class, $handle);
123
				$object->publishSingle();
0 ignored issues
show
Bug introduced by
The method publishSingle does only exist in Versioned, but not in DataObject.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
124
			}
125
		}
126
	}
127
128
	/**
129
	 * Check that the changeset includes the given items
130
	 *
131
	 * @param ChangeSet $cs
132
	 * @param array $match Array of object fixture keys with change type values
133
	 */
134
	protected function assertChangeSetLooksLike($cs, $match) {
135
		$items = $cs->Changes()->toArray();
136
137
		foreach($match as $key => $mode) {
138
			list($class, $identifier) = explode('.', $key);
139
			$object = $this->objFromFixture($class, $identifier);
140
141
			foreach($items as $i => $item) {
142
				if ($item->ObjectClass == $object->ClassName && $item->ObjectID == $object->ID && $item->Added == $mode) {
143
					unset($items[$i]);
144
					continue 2;
145
				}
146
			}
147
148
			throw new PHPUnit_Framework_ExpectationFailedException(
149
				'Change set didn\'t include expected item',
150
				new \SebastianBergmann\Comparator\ComparisonFailure(array('Class' => $class, 'ID' => $object->ID, 'Added' => $mode), null, "$key => $mode", '')
151
			);
152
		}
153
154
		if (count($items)) {
155
			$extra = [];
156
			foreach ($items as $item) $extra[] = ['Class' => $item->ObjectClass, 'ID' => $item->ObjectID, 'Added' => $item->Added, 'ChangeType' => $item->getChangeType()];
157
			throw new PHPUnit_Framework_ExpectationFailedException(
158
				'Change set included items that weren\'t expected',
159
				new \SebastianBergmann\Comparator\ComparisonFailure(array(), $extra, '', print_r($extra, true))
160
			);
161
		}
162
	}
163
164
	public function testRepeatedSyncIsNOP() {
165
		$this->publishAllFixtures();
166
167
		$cs = new ChangeSet();
168
		$cs->write();
169
170
		$base = $this->objFromFixture('ChangeSetTest_Base', 'base');
171
		$cs->addObject($base);
0 ignored issues
show
Bug introduced by
It seems like $base defined by $this->objFromFixture('C...eSetTest_Base', 'base') on line 170 can be null; however, ChangeSet::addObject() 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...
172
173
		$cs->sync();
174
		$this->assertChangeSetLooksLike($cs, [
175
			'ChangeSetTest_Base.base' => ChangeSetItem::EXPLICITLY
176
		]);
177
178
		$cs->sync();
179
		$this->assertChangeSetLooksLike($cs, [
180
			'ChangeSetTest_Base.base' => ChangeSetItem::EXPLICITLY
181
		]);
182
	}
183
184
	public function testSync() {
185
		$this->publishAllFixtures();
186
187
		$cs = new ChangeSet();
188
		$cs->write();
189
190
		$base = $this->objFromFixture('ChangeSetTest_Base', 'base');
191
192
		$cs->addObject($base);
0 ignored issues
show
Bug introduced by
It seems like $base defined by $this->objFromFixture('C...eSetTest_Base', 'base') on line 190 can be null; however, ChangeSet::addObject() 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...
193
		$cs->sync();
194
195
		$this->assertChangeSetLooksLike($cs, [
196
			'ChangeSetTest_Base.base' => ChangeSetItem::EXPLICITLY
197
		]);
198
199
		$end = $this->objFromFixture('ChangeSetTest_End', 'end1');
200
		$end->Baz = 3;
0 ignored issues
show
Documentation introduced by
The property Baz does not exist on object<DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
201
		$end->write();
202
203
		$cs->sync();
204
205
		$this->assertChangeSetLooksLike($cs, [
206
			'ChangeSetTest_Base.base' => ChangeSetItem::EXPLICITLY,
207
			'ChangeSetTest_End.end1' => ChangeSetItem::IMPLICITLY
208
		]);
209
210
		$endItem = $cs->Changes()->filter('ObjectClass', 'ChangeSetTest_End')->first();
211
212
		$this->assertEquals(
213
			[$base->ID],
214
			$endItem->ReferencedBy()->column("ID")
215
		);
216
217
		$this->assertDOSEquals([
218
			[
219
				'Added' => ChangeSetItem::EXPLICITLY,
220
				'ObjectClass' => 'ChangeSetTest_Base',
221
				'ObjectID' => $base->ID,
222
				'ChangeSetID' => $cs->ID
223
			]
224
		], $endItem->ReferencedBy());
225
	}
226
227
	/**
228
	 * Test that sync includes implicit items
229
	 */
230
	public function testIsSynced() {
231
		$this->publishAllFixtures();
232
233
		$cs = new ChangeSet();
234
		$cs->write();
235
236
		$base = $this->objFromFixture('ChangeSetTest_Base', 'base');
237
		$cs->addObject($base);
0 ignored issues
show
Bug introduced by
It seems like $base defined by $this->objFromFixture('C...eSetTest_Base', 'base') on line 236 can be null; however, ChangeSet::addObject() 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...
238
239
		$cs->sync();
240
		$this->assertChangeSetLooksLike($cs, [
241
			'ChangeSetTest_Base.base' => ChangeSetItem::EXPLICITLY
242
		]);
243
		$this->assertTrue($cs->isSynced());
244
245
		$end = $this->objFromFixture('ChangeSetTest_End', 'end1');
246
		$end->Baz = 3;
0 ignored issues
show
Documentation introduced by
The property Baz does not exist on object<DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
247
		$end->write();
248
		$this->assertFalse($cs->isSynced());
249
250
		$cs->sync();
251
252
		$this->assertChangeSetLooksLike($cs, [
253
			'ChangeSetTest_Base.base' => ChangeSetItem::EXPLICITLY,
254
			'ChangeSetTest_End.end1' => ChangeSetItem::IMPLICITLY
255
		]);
256
		$this->assertTrue($cs->isSynced());
257
	}
258
259
260
	public function testCanPublish() {
261
		// Create changeset containing all items (unpublished)
262
		$this->logInWithPermission('ADMIN');
263
		$changeSet = new ChangeSet();
264
		$changeSet->write();
265
		$base = $this->objFromFixture('ChangeSetTest_Base', 'base');
266
		$changeSet->addObject($base);
0 ignored issues
show
Bug introduced by
It seems like $base defined by $this->objFromFixture('C...eSetTest_Base', 'base') on line 265 can be null; however, ChangeSet::addObject() 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...
267
		$changeSet->sync();
268
		$this->assertEquals(5, $changeSet->Changes()->count());
269
270
		// Test un-authenticated user cannot publish
271
		Session::clear("loggedInAs");
272
		$this->assertFalse($changeSet->canPublish());
273
274
		// User with only one of the necessary permissions cannot publish
275
		$this->logInWithPermission('CMS_ACCESS_CampaignAdmin');
276
		$this->assertFalse($changeSet->canPublish());
277
		$this->logInWithPermission('PERM_canPublish');
278
		$this->assertFalse($changeSet->canPublish());
279
280
		// Test user with the necessary minimum permissions can login
281
		$this->logInWithPermission([
282
			'CMS_ACCESS_CampaignAdmin',
283
			'PERM_canPublish'
284
		]);
285
		$this->assertTrue($changeSet->canPublish());
286
	}
287
288
	public function testCanRevert() {
289
		$this->markTestSkipped("Requires ChangeSet::revert to be implemented first");
290
	}
291
292
	public function testCanEdit() {
293
		// Create changeset containing all items (unpublished)
294
		$this->logInWithPermission('ADMIN');
295
		$changeSet = new ChangeSet();
296
		$changeSet->write();
297
		$base = $this->objFromFixture('ChangeSetTest_Base', 'base');
298
		$changeSet->addObject($base);
0 ignored issues
show
Bug introduced by
It seems like $base defined by $this->objFromFixture('C...eSetTest_Base', 'base') on line 297 can be null; however, ChangeSet::addObject() 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...
299
		$changeSet->sync();
300
		$this->assertEquals(5, $changeSet->Changes()->count());
301
302
		// Check canEdit
303
		Session::clear("loggedInAs");
304
		$this->assertFalse($changeSet->canEdit());
305
		$this->logInWithPermission('SomeWrongPermission');
306
		$this->assertFalse($changeSet->canEdit());
307
		$this->logInWithPermission('CMS_ACCESS_CampaignAdmin');
308
		$this->assertTrue($changeSet->canEdit());
309
	}
310
311
	public function testCanCreate() {
312
		// Check canCreate
313
		Session::clear("loggedInAs");
314
		$this->assertFalse(ChangeSet::singleton()->canCreate());
315
		$this->logInWithPermission('SomeWrongPermission');
316
		$this->assertFalse(ChangeSet::singleton()->canCreate());
317
		$this->logInWithPermission('CMS_ACCESS_CampaignAdmin');
318
		$this->assertTrue(ChangeSet::singleton()->canCreate());
319
	}
320
321
	public function testCanDelete() {
322
		// Create changeset containing all items (unpublished)
323
		$this->logInWithPermission('ADMIN');
324
		$changeSet = new ChangeSet();
325
		$changeSet->write();
326
		$base = $this->objFromFixture('ChangeSetTest_Base', 'base');
327
		$changeSet->addObject($base);
0 ignored issues
show
Bug introduced by
It seems like $base defined by $this->objFromFixture('C...eSetTest_Base', 'base') on line 326 can be null; however, ChangeSet::addObject() 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...
328
		$changeSet->sync();
329
		$this->assertEquals(5, $changeSet->Changes()->count());
330
331
		// Check canDelete
332
		Session::clear("loggedInAs");
333
		$this->assertFalse($changeSet->canDelete());
334
		$this->logInWithPermission('SomeWrongPermission');
335
		$this->assertFalse($changeSet->canDelete());
336
		$this->logInWithPermission('CMS_ACCESS_CampaignAdmin');
337
		$this->assertTrue($changeSet->canDelete());
338
	}
339
340
	public function testCanView() {
341
		// Create changeset containing all items (unpublished)
342
		$this->logInWithPermission('ADMIN');
343
		$changeSet = new ChangeSet();
344
		$changeSet->write();
345
		$base = $this->objFromFixture('ChangeSetTest_Base', 'base');
346
		$changeSet->addObject($base);
0 ignored issues
show
Bug introduced by
It seems like $base defined by $this->objFromFixture('C...eSetTest_Base', 'base') on line 345 can be null; however, ChangeSet::addObject() 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...
347
		$changeSet->sync();
348
		$this->assertEquals(5, $changeSet->Changes()->count());
349
350
		// Check canView
351
		Session::clear("loggedInAs");
352
		$this->assertFalse($changeSet->canView());
353
		$this->logInWithPermission('SomeWrongPermission');
354
		$this->assertFalse($changeSet->canView());
355
		$this->logInWithPermission('CMS_ACCESS_CampaignAdmin');
356
		$this->assertTrue($changeSet->canView());
357
	}
358
359
	public function testPublish() {
360
		$this->publishAllFixtures();
361
362
		$base = $this->objFromFixture('ChangeSetTest_Base', 'base');
363
		$baseID = $base->ID;
364
		$baseBefore = $base->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
365
		$end1 = $this->objFromFixture('ChangeSetTest_End', 'end1');
366
		$end1ID = $end1->ID;
367
		$end1Before = $end1->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
368
369
		// Create a new changest
370
		$changeset = new ChangeSet();
371
		$changeset->write();
372
		$changeset->addObject($base);
373
		$changeset->addObject($end1);
374
375
		// Make a lot of changes
376
		// - ChangeSetTest_Base.base modified
377
		// - ChangeSetTest_End.end1 deleted
378
		// - new ChangeSetTest_Mid added
379
		$base->Foo = 343;
0 ignored issues
show
Documentation introduced by
The property Foo does not exist on object<DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
380
		$base->write();
381
		$baseAfter = $base->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
382
		$midNew = new ChangeSetTest_Mid();
383
		$midNew->Bar = 39;
0 ignored issues
show
Documentation introduced by
The property Bar does not exist on object<ChangeSetTest_Mid>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
384
		$midNew->write();
385
		$midNewID = $midNew->ID;
386
		$midNewAfter = $midNew->Version;
0 ignored issues
show
Documentation introduced by
The property Version does not exist on object<ChangeSetTest_Mid>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
387
		$end1->delete();
388
389
		$changeset->addObject($midNew);
390
391
		// Publish
392
		$this->logInWithPermission('ADMIN');
393
		$this->assertTrue($changeset->canPublish());
394
		$this->assertTrue($changeset->isSynced());
395
		$changeset->publish();
396
		$this->assertEquals(ChangeSet::STATE_PUBLISHED, $changeset->State);
397
398
		// Check each item has the correct before/after version applied
399
		$baseChange = $changeset->Changes()->filter([
400
			'ObjectClass' => 'ChangeSetTest_Base',
401
			'ObjectID' => $baseID,
402
		])->first();
403
		$this->assertEquals((int)$baseBefore, (int)$baseChange->VersionBefore);
404
		$this->assertEquals((int)$baseAfter, (int)$baseChange->VersionAfter);
405
		$this->assertEquals((int)$baseChange->VersionBefore + 1, (int)$baseChange->VersionAfter);
406
		$this->assertEquals(
407
			(int)$baseChange->VersionAfter,
408
			(int)Versioned::get_versionnumber_by_stage('ChangeSetTest_Base', Versioned::LIVE, $baseID)
409
		);
410
411
		$end1Change = $changeset->Changes()->filter([
412
			'ObjectClass' => 'ChangeSetTest_End',
413
			'ObjectID' => $end1ID,
414
		])->first();
415
		$this->assertEquals((int)$end1Before, (int)$end1Change->VersionBefore);
416
		$this->assertEquals(0, (int)$end1Change->VersionAfter);
417
		$this->assertEquals(
418
			0,
419
			(int)Versioned::get_versionnumber_by_stage('ChangeSetTest_End', Versioned::LIVE, $end1ID)
420
		);
421
422
		$midNewChange = $changeset->Changes()->filter([
423
			'ObjectClass' => 'ChangeSetTest_Mid',
424
			'ObjectID' => $midNewID,
425
		])->first();
426
		$this->assertEquals(0, (int)$midNewChange->VersionBefore);
427
		$this->assertEquals((int)$midNewAfter, (int)$midNewChange->VersionAfter);
428
		$this->assertEquals(
429
			(int)$midNewAfter,
430
			(int)Versioned::get_versionnumber_by_stage('ChangeSetTest_Mid', Versioned::LIVE, $midNewID)
431
		);
432
433
		// Test trying to re-publish is blocked
434
		$this->setExpectedException(
435
			'BadMethodCallException',
436
			"ChangeSet can't be published if it has been already published or reverted."
437
		);
438
		$changeset->publish();
439
	}
440
441
}
442