Ticket 4011 - Adding matchers support for Hash::remove() and Hash::insert()

This commit is contained in:
Chen Cohen 2013-08-23 13:27:06 +03:00
parent 6d45953525
commit a0014e7a30
3 changed files with 104 additions and 13 deletions

View file

@ -443,10 +443,10 @@ class CakeSessionTest extends CakeTestCase {
public function testKeyExploit() {
$key = "a'] = 1; phpinfo(); \$_SESSION['a";
$result = TestCakeSession::write($key, 'haxored');
$this->assertTrue($result);
$this->assertFalse($result);
$result = TestCakeSession::read($key);
$this->assertEquals('haxored', $result);
$this->assertNull($result);
}
/**

View file

@ -1297,6 +1297,23 @@ class HashTest extends CakeTestCase {
$result = Hash::insert($data, '{n}.Comment.{n}.insert', 'value');
$this->assertEquals('value', $result[0]['Comment'][0]['insert']);
$this->assertEquals('value', $result[0]['Comment'][1]['insert']);
$data = array(
0 => array('Item' => array('id' => 1, 'title' => 'first')),
1 => array('Item' => array('id' => 2, 'title' => 'second')),
2 => array('Item' => array('id' => 3, 'title' => 'third')),
3 => array('Item' => array('id' => 4, 'title' => 'fourth')),
4 => array('Item' => array('id' => 5, 'title' => 'fifth')),
);
$result = Hash::insert($data, '{n}.Item[id=/\b2|\b4/]', array('test' => 2));
$expected = array(
0 => array('Item' => array('id' => 1, 'title' => 'first')),
1 => array('Item' => array('id' => 2, 'title' => 'second', 'test' => 2)),
2 => array('Item' => array('id' => 3, 'title' => 'third')),
3 => array('Item' => array('id' => 4, 'title' => 'fourth', 'test' => 2)),
4 => array('Item' => array('id' => 5, 'title' => 'fifth')),
);
$this->assertEquals($expected, $result);
}
/**
@ -1360,6 +1377,23 @@ class HashTest extends CakeTestCase {
$result = Hash::remove($a, 'pages.2.vars');
$expected = $a;
$this->assertEquals($expected, $result);
$a = array(
0 => array(
'name' => 'pages'
),
1 => array(
'name' => 'files'
)
);
$result = Hash::remove($a, '{n}[name=files]');
$expected = array(
0 => array(
'name' => 'pages'
)
);
$this->assertEquals($expected, $result);
}
/**
@ -1379,6 +1413,22 @@ class HashTest extends CakeTestCase {
$this->assertFalse(isset($result[0]['Article']['user_id']));
$this->assertFalse(isset($result[0]['Article']['title']));
$this->assertFalse(isset($result[0]['Article']['body']));
$data = array(
0 => array('Item' => array('id' => 1, 'title' => 'first')),
1 => array('Item' => array('id' => 2, 'title' => 'second')),
2 => array('Item' => array('id' => 3, 'title' => 'third')),
3 => array('Item' => array('id' => 4, 'title' => 'fourth')),
4 => array('Item' => array('id' => 5, 'title' => 'fifth')),
);
$result = Hash::remove($data, '{n}.Item[id=/\b2|\b4/]');
$expected = array(
0 => array('Item' => array('id' => 1, 'title' => 'first')),
2 => array('Item' => array('id' => 3, 'title' => 'third')),
4 => array('Item' => array('id' => 5, 'title' => 'fifth')),
);
$this->assertEquals($result, $expected);
}
/**

View file

@ -109,12 +109,7 @@ class Hash {
foreach ($tokens as $token) {
$next = array();
$conditions = false;
$position = strpos($token, '[');
if ($position !== false) {
$conditions = substr($token, $position);
$token = substr($token, 0, $position);
}
list($token, $conditions) = self::_splitConditions($token);
foreach ($context[$_key] as $item) {
foreach ((array)$item as $k => $v) {
@ -139,6 +134,22 @@ class Hash {
}
return $context[$_key];
}
/**
* Split token conditions
*
* @param string $token the token being splitted.
* @return array array(token, conditions) with token splitted
*/
protected static function _splitConditions($token) {
$conditions = false;
$position = strpos($token, '[');
if ($position !== false) {
$conditions = substr($token, $position);
$token = substr($token, 0, $position);
}
return array($token, $conditions);
}
/**
* Check a key against a token.
@ -222,16 +233,31 @@ class Hash {
* @return array The data with $values inserted.
*/
public static function insert(array $data, $path, $values = null) {
$tokens = explode('.', $path);
if (strpos($path, '{') === false) {
if (strpos($path, '[') === false) {
$tokens = explode('.', $path);
} else {
$tokens = String::tokenize($path, '.', '[', ']');
}
if (strpos($path, '{') === false && strpos($path, '[') === false) {
return self::_simpleOp('insert', $data, $tokens, $values);
}
$token = array_shift($tokens);
$nextPath = implode('.', $tokens);
list($token, $conditions) = self::_splitConditions($token);
foreach ($data as $k => $v) {
if (self::_matchToken($k, $token)) {
$data[$k] = self::insert($v, $nextPath, $values);
if ($conditions) {
if (self::_matches($v, $conditions)) {
$data[$k] = array_merge($v, $values);
continue;
}
} else {
$data[$k] = self::insert($v, $nextPath, $values);
}
}
}
return $data;
@ -290,17 +316,32 @@ class Hash {
* @return array The modified array.
*/
public static function remove(array $data, $path) {
$tokens = explode('.', $path);
if (strpos($path, '{') === false) {
if (strpos($path, '[') === false) {
$tokens = explode('.', $path);
} else {
$tokens = String::tokenize($path, '.', '[', ']');
}
if (strpos($path, '{') === false && strpos($path, '[') === false) {
return self::_simpleOp('remove', $data, $tokens);
}
$token = array_shift($tokens);
$nextPath = implode('.', $tokens);
list($token, $conditions) = self::_splitConditions($token);
foreach ($data as $k => $v) {
$match = self::_matchToken($k, $token);
if ($match && is_array($v)) {
if ($conditions && self::_matches($v, $conditions)) {
unset($data[$k]);
continue;
}
$data[$k] = self::remove($v, $nextPath);
if (empty($data[$k])) {
unset($data[$k]);
}
} elseif ($match) {
unset($data[$k]);
}