Skip to content

Commit 9a33422

Browse files
committed
Merge pull request #38 from phpcr/absolutize-path
add absolutizePath method to path helper
2 parents 6421d72 + 790b670 commit 9a33422

File tree

2 files changed

+138
-24
lines changed

2 files changed

+138
-24
lines changed

src/PHPCR/Util/PathHelper.php

Lines changed: 74 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ public static function assertValidLocalName($name, $throw = true)
134134
* just to return false.
135135
*
136136
* @return string The normalized path or false if $throw was false and the path invalid
137+
*
138+
* @throws RepositoryException if the path is not a valid absolute path and
139+
* $throw is true
137140
*/
138141
public static function normalizePath($path, $destination = false, $throw = true)
139142
{
@@ -142,30 +145,41 @@ public static function normalizePath($path, $destination = false, $throw = true)
142145
}
143146

144147
if ('/' === $path) {
145-
$normalizedPath = '/';
146-
} else {
147-
$finalParts= array();
148-
$parts = explode('/', $path);
149-
150-
foreach ($parts as $pathPart) {
151-
switch ($pathPart) {
152-
case '.':
153-
break;
154-
case '..':
155-
if (count($finalParts) > 1) {
156-
// do not remove leading slash. "/.." is "/", not ""
157-
array_pop($finalParts);
158-
}
159-
break;
160-
default:
161-
$finalParts[] = $pathPart;
162-
break;
163-
}
148+
149+
return '/';
150+
}
151+
152+
if ('/' !== $path[0]) {
153+
if ($throw) {
154+
throw new RepositoryException("Not an absolute path '$path'");
155+
}
156+
157+
return false;
158+
}
159+
160+
$finalParts= array();
161+
$parts = explode('/', $path);
162+
163+
foreach ($parts as $pathPart) {
164+
switch ($pathPart) {
165+
case '.':
166+
break;
167+
case '..':
168+
if (count($finalParts) > 1) {
169+
// do not remove leading slash. "/.." is "/", not ""
170+
array_pop($finalParts);
171+
}
172+
break;
173+
default:
174+
$finalParts[] = $pathPart;
175+
break;
164176
}
165-
$normalizedPath = count($finalParts) > 1 ?
166-
implode('/', $finalParts) :
167-
'/'; // first element is always the empty-name root element
168177
}
178+
$normalizedPath = count($finalParts) > 1 ?
179+
implode('/', $finalParts) :
180+
'/' // first element is always the empty-name root element. this might have been a path like /x/..
181+
;
182+
169183
if (! self::assertValidAbsolutePath($normalizedPath, $destination, $throw)) {
170184

171185
return false;
@@ -174,6 +188,35 @@ public static function normalizePath($path, $destination = false, $throw = true)
174188
return $normalizedPath;
175189
}
176190

191+
/**
192+
* In addition to normalizing and validating, this method combines the path
193+
* with a context if it is not absolute.
194+
*
195+
* @param string $path A relative or absolute path
196+
* @param string $context The absolute path context to make $path absolute if needed
197+
* @param bool $destination whether this is a destination path (by copy or
198+
* move), meaning [] is not allowed in validation.
199+
* @param bool $throw whether to throw an exception if validation fails or
200+
* just to return false.
201+
*
202+
* @return string The normalized, absolute path or false if $throw was
203+
* false and the path invalid
204+
*
205+
* @throws RepositoryException if the path can not be made into a valid
206+
* absolute path and $throw is true
207+
*/
208+
public static function absolutizePath($path, $context, $destination = false, $throw = true)
209+
{
210+
if (! $path) {
211+
throw new RepositoryException('empty path');
212+
}
213+
if ('/' !== $path[0]) {
214+
$path = ('/' === $context) ? "/$path" : "$context/$path";
215+
}
216+
217+
return self::normalizePath($path, $destination, $throw);
218+
}
219+
177220
/**
178221
* Get the parent path of a valid absolute path.
179222
*
@@ -185,10 +228,17 @@ public static function getParentPath($path)
185228
{
186229
if ('/' === $path) {
187230

188-
return $path;
231+
return '/';
232+
}
233+
234+
$pos = strrpos($path, '/');
235+
236+
if (0 === $pos) {
237+
238+
return '/';
189239
}
190240

191-
return substr($path, 0, strrpos($path, '/'));
241+
return substr($path, 0, $pos);
192242
}
193243

194244
/**

tests/PHPCR/Tests/Util/PathHelperTest.php

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,23 @@
66

77
class PathHelperTest extends \PHPUnit_Framework_TestCase
88
{
9+
// assertValidPath tests
10+
911
public function testAssertValidPath()
1012
{
1113
$this->assertTrue(PathHelper::assertValidAbsolutePath('/parent/child'));
1214
}
15+
1316
public function testAssertValidPathRoot()
1417
{
1518
$this->assertTrue(PathHelper::assertValidAbsolutePath('/'));
1619
}
20+
1721
public function testAssertValidPathNamespaced()
1822
{
1923
$this->assertTrue(PathHelper::assertValidAbsolutePath('/jcr:foo_/b-a/0^.txt'));
2024
}
25+
2126
public function testAssertValidPathIndexed()
2227
{
2328
$this->assertTrue(PathHelper::assertValidAbsolutePath('/parent[7]/child'));
@@ -30,75 +35,88 @@ public function testAssertValidTargetPathNoIndex()
3035
{
3136
PathHelper::assertValidAbsolutePath('/parent/child[7]', true);
3237
}
38+
3339
/**
3440
* @expectedException \PHPCR\RepositoryException
3541
*/
3642
public function testAssertValidPathNotAbsolute()
3743
{
3844
PathHelper::assertValidAbsolutePath('parent');
3945
}
46+
4047
/**
4148
* @expectedException \PHPCR\RepositoryException
4249
*/
4350
public function testAssertValidPathDouble()
4451
{
4552
PathHelper::assertValidAbsolutePath('/parent//child');
4653
}
54+
4755
/**
4856
* @expectedException \PHPCR\RepositoryException
4957
*/
5058
public function testAssertValidPathParent()
5159
{
5260
PathHelper::assertValidAbsolutePath('/parent/../child');
5361
}
62+
5463
/**
5564
* @expectedException \PHPCR\RepositoryException
5665
*/
5766
public function testAssertValidPathSelf()
5867
{
5968
PathHelper::assertValidAbsolutePath('/parent/./child');
6069
}
70+
6171
/**
6272
* @expectedException \PHPCR\RepositoryException
6373
*/
6474
public function testAssertValidPathTrailing()
6575
{
6676
PathHelper::assertValidAbsolutePath('/parent/child/');
6777
}
78+
6879
public function testAssertValidPathNoThrow()
6980
{
7081
$this->assertFalse(PathHelper::assertValidAbsolutePath('parent', false, false));
7182
}
7283

84+
// assertValidLocalName tests
85+
7386
public function testAssertValidLocalName()
7487
{
7588
$this->assertTrue(PathHelper::assertValidLocalName('nodename'));
7689
}
90+
7791
public function testAssertValidLocalNameRootnode()
7892
{
7993
$this->assertTrue(PathHelper::assertValidLocalName(''));
8094
}
95+
8196
/**
8297
* @expectedException \PHPCR\RepositoryException
8398
*/
8499
public function testAssertValidLocalNameNamespaced()
85100
{
86101
$this->assertTrue(PathHelper::assertValidLocalName('jcr:nodename'));
87102
}
103+
88104
/**
89105
* @expectedException \PHPCR\RepositoryException
90106
*/
91107
public function testAssertValidLocalNamePath()
92108
{
93109
$this->assertTrue(PathHelper::assertValidLocalName('/path'));
94110
}
111+
95112
/**
96113
* @expectedException \PHPCR\RepositoryException
97114
*/
98115
public function testAssertValidLocalNameSelf()
99116
{
100117
PathHelper::assertValidLocalName('.');
101118
}
119+
102120
/**
103121
* @expectedException \PHPCR\RepositoryException
104122
*/
@@ -107,13 +125,16 @@ public function testAssertValidLocalNameParent()
107125
PathHelper::assertValidLocalName('..');
108126
}
109127

128+
// normalizePath tests
129+
110130
/**
111131
* @dataProvider dataproviderNormalizePath
112132
*/
113133
public function testNormalizePath($inputPath, $outputPath)
114134
{
115135
$this->assertSame($outputPath, PathHelper::normalizePath($inputPath));
116136
}
137+
117138
public static function dataproviderNormalizePath()
118139
{
119140
return array(
@@ -131,13 +152,23 @@ public function testNormalizePathInvalid()
131152
{
132153
PathHelper::normalizePath('foo/bar');
133154
}
155+
156+
/**
157+
* @expectedException \PHPCR\RepositoryException
158+
*/
159+
public function testNormalizePathShortInvalid()
160+
{
161+
PathHelper::normalizePath('bar');
162+
}
163+
134164
/**
135165
* @expectedException \PHPCR\RepositoryException
136166
*/
137167
public function testNormalizePathTrailing()
138168
{
139169
PathHelper::normalizePath('/foo/bar/');
140170
}
171+
141172
/**
142173
* @expectedException \PHPCR\RepositoryException
143174
*/
@@ -146,27 +177,60 @@ public function testNormalizePathEmpty()
146177
PathHelper::normalizePath('');
147178
}
148179

180+
// absolutizePath tests
181+
182+
/**
183+
* @dataProvider dataproviderAbsolutizePath
184+
*/
185+
public function testAbsolutizePath($inputPath, $context, $outputPath)
186+
{
187+
$this->assertSame($outputPath, PathHelper::absolutizePath($inputPath, $context));
188+
}
189+
190+
public static function dataproviderAbsolutizePath()
191+
{
192+
return array(
193+
array('/../foo', '/', '/foo'),
194+
array('../', '/', '/'),
195+
array('../foo/bar', '/baz', '/foo/bar'),
196+
array('foo/./bar', '/baz', '/baz/foo/bar'),
197+
);
198+
}
199+
200+
// getParentPath tests
201+
149202
public function testGetParentPath()
150203
{
151204
$this->assertEquals('/parent', PathHelper::getParentPath('/parent/child'));
152205
}
206+
153207
public function testGetParentPathNamespaced()
154208
{
155209
$this->assertEquals('/jcr:parent', PathHelper::getParentPath('/jcr:parent/ns:child'));
156210
}
211+
212+
public function testGetParentPathNodeAtRoot()
213+
{
214+
$this->assertEquals('/', PathHelper::getParentPath('/parent'));
215+
}
216+
157217
public function testGetParentPathRoot()
158218
{
159219
$this->assertEquals('/', PathHelper::getParentPath('/'));
160220
}
161221

222+
// getNodeName tests
223+
162224
public function testGetNodeName()
163225
{
164226
$this->assertEquals('child', PathHelper::getNodeName('/parent/child'));
165227
}
228+
166229
public function testGetNodeNameNamespaced()
167230
{
168231
$this->assertEquals('ns:child', PathHelper::getNodeName('/parent/ns:child'));
169232
}
233+
170234
public function testGetNodeNameRoot()
171235
{
172236
$this->assertEquals('', PathHelper::getNodeName('/'));

0 commit comments

Comments
 (0)