Extend Name::slice() to support negative length+offset

This commit is contained in:
Nikita Popov 2016-07-25 14:47:24 +02:00
parent 818ef2e692
commit c9fea2ef67
2 changed files with 57 additions and 10 deletions

View File

@ -165,17 +165,31 @@ class Name extends NodeAbstract
* If the slice is empty, a Name with an empty parts array is returned. While this is
* meaningless in itself, it works correctly in conjunction with concat().
*
* @param int $offset Offset to start the slice at
* Offset and length have the same meaning as in array_slice().
*
* @param int $offset Offset to start the slice at (may be negative)
* @param int|null $length Length of the slice (may be negative)
*
* @return static Sliced name
*/
public function slice($offset) {
// TODO negative offset and length
if ($offset < 0 || $offset > count($this->parts)) {
public function slice($offset, $length = null) {
$numParts = count($this->parts);
$realOffset = $offset < 0 ? $offset + $numParts : $offset;
if ($realOffset < 0 || $realOffset > $numParts) {
throw new \OutOfBoundsException(sprintf('Offset %d is out of bounds', $offset));
}
return new static(array_slice($this->parts, $offset), $this->attributes);
if (null === $length) {
$realLength = $numParts - $realOffset;
} else {
$realLength = $length < 0 ? $length + $numParts - $realOffset : $length;
if ($realLength < 0 || $realLength > $numParts) {
throw new \OutOfBoundsException(sprintf('Length %d is out of bounds', $length));
}
}
return new static(array_slice($this->parts, $realOffset, $realLength), $this->attributes);
}
/**

View File

@ -96,20 +96,53 @@ class NameTest extends \PHPUnit_Framework_TestCase
}
public function testSlice() {
$name = new Name('foo\bar');
$this->assertEquals(new Name('foo\bar'), $name->slice(0));
$this->assertEquals(new Name('bar'), $name->slice(1));
$this->assertEquals(new Name([]), $name->slice(2));
$name = new Name('foo\bar\baz');
$this->assertEquals(new Name('foo\bar\baz'), $name->slice(0));
$this->assertEquals(new Name('bar\baz'), $name->slice(1));
$this->assertEquals(new Name([]), $name->slice(3));
$this->assertEquals(new Name('foo\bar\baz'), $name->slice(-3));
$this->assertEquals(new Name('bar\baz'), $name->slice(-2));
$this->assertEquals(new Name('foo\bar'), $name->slice(0, -1));
$this->assertEquals(new Name([]), $name->slice(0, -3));
$this->assertEquals(new Name('bar'), $name->slice(1, -1));
$this->assertEquals(new Name([]), $name->slice(1, -2));
$this->assertEquals(new Name('bar'), $name->slice(-2, 1));
$this->assertEquals(new Name('bar'), $name->slice(-2, -1));
$this->assertEquals(new Name([]), $name->slice(-2, -2));
}
/**
* @expectedException \OutOfBoundsException
* @expectedExceptionMessage Offset 4 is out of bounds
*/
public function testSliceException() {
public function testSliceOffsetTooLarge() {
(new Name('foo\bar\baz'))->slice(4);
}
/**
* @expectedException \OutOfBoundsException
* @expectedExceptionMessage Offset -4 is out of bounds
*/
public function testSliceOffsetTooSmall() {
(new Name('foo\bar\baz'))->slice(-4);
}
/**
* @expectedException \OutOfBoundsException
* @expectedExceptionMessage Length 4 is out of bounds
*/
public function testSliceLengthTooLarge() {
(new Name('foo\bar\baz'))->slice(0, 4);
}
/**
* @expectedException \OutOfBoundsException
* @expectedExceptionMessage Length -4 is out of bounds
*/
public function testSliceLengthTooSmall() {
(new Name('foo\bar\baz'))->slice(0, -4);
}
public function testConcat() {
$this->assertEquals(new Name('foo\bar\baz'), Name::concat('foo', 'bar\baz'));
$this->assertEquals(