Skip to content

Commit

Permalink
Ticket 4011 - Adding matchers support for Hash::remove() and Hash::in…
Browse files Browse the repository at this point in the history
…sert()
  • Loading branch information
0xcc77 committed Aug 23, 2013
1 parent 6d45953 commit a0014e7
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 13 deletions.
4 changes: 2 additions & 2 deletions lib/Cake/Test/Case/Model/Datasource/CakeSessionTest.php
Expand Up @@ -443,10 +443,10 @@ public function testCheckEmpty() {
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);
}

/**
Expand Down
50 changes: 50 additions & 0 deletions lib/Cake/Test/Case/Utility/HashTest.php
Expand Up @@ -1297,6 +1297,23 @@ public function testInsertMulti() {
$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);
}

/**
Expand Down Expand Up @@ -1360,6 +1377,23 @@ public function testRemove() {
$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);
}

/**
Expand All @@ -1379,6 +1413,22 @@ public function testRemoveMulti() {
$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);
}

/**
Expand Down
63 changes: 52 additions & 11 deletions lib/Cake/Utility/Hash.php
Expand Up @@ -109,12 +109,7 @@ public static function extract(array $data, $path) {
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) {
Expand All @@ -139,6 +134,22 @@ public static function extract(array $data, $path) {
}
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.
Expand Down Expand Up @@ -222,16 +233,31 @@ protected static function _matches(array $data, $selector) {
* @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;
Expand Down Expand Up @@ -290,17 +316,32 @@ protected static function _simpleOp($op, $data, $path, $values = null) {
* @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]);
}
Expand Down

0 comments on commit a0014e7

Please sign in to comment.