mirror of
https://github.com/kamilwylegala/cakephp2-php8.git
synced 2025-01-18 18:46:17 +00:00
Added initial XPath support for Set::extract
git-svn-id: https://svn.cakephp.org/repo/branches/1.2.x.x@6567 3807eeeb-6ff5-0310-8944-8be069107fe0
This commit is contained in:
parent
3a67bdc06d
commit
3daf495488
2 changed files with 268 additions and 4 deletions
|
@ -359,6 +359,125 @@ class Set extends Object {
|
||||||
}
|
}
|
||||||
return $out;
|
return $out;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* undocumented function
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* @param string $data
|
||||||
|
* @param string $options
|
||||||
|
* @return void
|
||||||
|
* @author Felix
|
||||||
|
*/
|
||||||
|
|
||||||
|
function extract($path, $data = null, $options = array()) {
|
||||||
|
if (is_array($path) || empty($data)) {
|
||||||
|
return Set::classicExtract($path, $data);
|
||||||
|
}
|
||||||
|
$contexts = $data;
|
||||||
|
$options = am(array('flatten' => true), $options);
|
||||||
|
if (!isset($contexts[0])) {
|
||||||
|
$contexts = array($data);
|
||||||
|
}
|
||||||
|
if (is_string($path)) {
|
||||||
|
$last = substr($path, -1, 1);
|
||||||
|
if ($last == '*' && substr($path, -2, 1) != '/') {
|
||||||
|
$path = substr($path, 0, -1);
|
||||||
|
} else {
|
||||||
|
$last = false;
|
||||||
|
}
|
||||||
|
$tokens = array_slice(explode('/', $path), 1);
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
$token = array_shift($tokens);
|
||||||
|
$conditions = false;
|
||||||
|
if (preg_match_all('/\[([^\]]+)\]/', $token, $m)) {
|
||||||
|
$conditions = $m[1];
|
||||||
|
$token = substr($token, 0, strpos($token, '['));
|
||||||
|
}
|
||||||
|
|
||||||
|
$matches = array();
|
||||||
|
foreach ($contexts as $i => $context) {
|
||||||
|
if (!isset($context['trace'])) {
|
||||||
|
$context = array('trace' => array(), 'item' => $context, 'key' => null);
|
||||||
|
}
|
||||||
|
if ($token == '..') {
|
||||||
|
$context['item'] = Set::extract(join('/', $context['trace']), $data);
|
||||||
|
$context['key'] = array_pop($context['trace']);
|
||||||
|
$context['item'] = $context['item'][0][$context['key']];
|
||||||
|
$matches[] = $context;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (array_key_exists($token, $context['item']) && (!$conditions || Set::matches($conditions, $context['item'][$token], $i+1))) {
|
||||||
|
$context['trace'][] = $context['key'];
|
||||||
|
$context['key'] = $token;
|
||||||
|
$context['item'] = $context['item'][$token];
|
||||||
|
$matches[] = $context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty($tokens)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$contexts = $matches;
|
||||||
|
} while(1);
|
||||||
|
|
||||||
|
$r = array();
|
||||||
|
foreach ($matches as $match) {
|
||||||
|
if (!$options['flatten'] || is_array($match['item'])) {
|
||||||
|
$r[] = array($match['key'] => $match['item']);
|
||||||
|
} else {
|
||||||
|
$r[] = $match['item'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This function can be used to see if a single item or a given xpath match certain conditions.
|
||||||
|
*
|
||||||
|
* @param mixed $conditions An array of condition strings
|
||||||
|
* @param array $data
|
||||||
|
* @param integer $i Optional: The 'nth'-number of the item being matched.
|
||||||
|
* @return boolean
|
||||||
|
* @author Felix
|
||||||
|
*/
|
||||||
|
|
||||||
|
function matches($conditions, $data = array(), $i = null) {
|
||||||
|
if (empty($conditions)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (is_string($conditions)) {
|
||||||
|
return !!Set::extract($conditions, $data);
|
||||||
|
}
|
||||||
|
foreach ($conditions as $condition) {
|
||||||
|
if (!preg_match('/(.+?)([><!]?[=]|[><])(.+)/', $condition, $match)) {
|
||||||
|
if (ctype_digit($condition)) {
|
||||||
|
if ($i != $condition) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} elseif (preg_match_all('/(?:^[0-9]+|(?<=,)[0-9]+)/', $condition, $matches)) {
|
||||||
|
return in_array($i, $matches[0]);
|
||||||
|
} elseif (!array_key_exists($condition, $data)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
list(,$key,$op,$expected) = $match;
|
||||||
|
$val = $data[$key];
|
||||||
|
if ($op == '=' && $val != $expected) {
|
||||||
|
return false;
|
||||||
|
} elseif ($op == '!=' && $val == $expected) {
|
||||||
|
return false;
|
||||||
|
} elseif ($op == '>' && $val <= $expected) {
|
||||||
|
return false;
|
||||||
|
} elseif ($op == '<' && $val >= $expected) {
|
||||||
|
return false;
|
||||||
|
} elseif ($op == '<=' && $val > $expected) {
|
||||||
|
return false;
|
||||||
|
} elseif ($op == '>=' && $val < $expected) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Gets a value from an array or object that is contained in a given path using an array path syntax, i.e.:
|
* Gets a value from an array or object that is contained in a given path using an array path syntax, i.e.:
|
||||||
* "{n}.Person.{[a-z]+}" - Where "{n}" represents a numeric key, "Person" represents a string literal,
|
* "{n}.Person.{[a-z]+}" - Where "{n}" represents a numeric key, "Person" represents a string literal,
|
||||||
|
@ -370,7 +489,7 @@ class Set extends Object {
|
||||||
* @return array Extracted data
|
* @return array Extracted data
|
||||||
* @access public
|
* @access public
|
||||||
*/
|
*/
|
||||||
function extract($data, $path = null) {
|
function classicExtract($data, $path = null) {
|
||||||
if ($path === null && is_a($this, 'set')) {
|
if ($path === null && is_a($this, 'set')) {
|
||||||
$path = $data;
|
$path = $data;
|
||||||
$data = $this->get();
|
$data = $this->get();
|
||||||
|
@ -405,7 +524,7 @@ class Set extends Object {
|
||||||
if (empty($tmpPath)) {
|
if (empty($tmpPath)) {
|
||||||
$tmp[] = $val;
|
$tmp[] = $val;
|
||||||
} else {
|
} else {
|
||||||
$tmp[] = Set::extract($val, $tmpPath);
|
$tmp[] = Set::classicExtract($val, $tmpPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -417,7 +536,7 @@ class Set extends Object {
|
||||||
if (empty($tmpPath)) {
|
if (empty($tmpPath)) {
|
||||||
$tmp[] = $val;
|
$tmp[] = $val;
|
||||||
} else {
|
} else {
|
||||||
$tmp[] = Set::extract($val, $tmpPath);
|
$tmp[] = Set::classicExtract($val, $tmpPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -431,7 +550,7 @@ class Set extends Object {
|
||||||
if (empty($tmpPath)) {
|
if (empty($tmpPath)) {
|
||||||
$tmp[$j] = $val;
|
$tmp[$j] = $val;
|
||||||
} else {
|
} else {
|
||||||
$tmp[$j] = Set::extract($val, $tmpPath);
|
$tmp[$j] = Set::classicExtract($val, $tmpPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -236,6 +236,151 @@ class SetTest extends UnitTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
function testExtract() {
|
function testExtract() {
|
||||||
|
$a = array(
|
||||||
|
array(
|
||||||
|
'Article' => array('id' => '1', 'user_id' => '1', 'title' => 'First Article', 'body' => 'First Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'),
|
||||||
|
'User' => array('id' => '1', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'),
|
||||||
|
'Comment' => array(
|
||||||
|
array('id' => '1', 'article_id' => '1', 'user_id' => '2', 'comment' => 'First Comment for First Article', 'published' => 'Y', 'created' => '2007-03-18 10:45:23', 'updated' => '2007-03-18 10:47:31'),
|
||||||
|
array('id' => '2', 'article_id' => '1', 'user_id' => '4', 'comment' => 'Second Comment for First Article', 'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31'),
|
||||||
|
),
|
||||||
|
'Tag' => array(
|
||||||
|
array('id' => '1', 'tag' => 'tag1', 'created' => '2007-03-18 12:22:23', 'updated' => '2007-03-18 12:24:31'),
|
||||||
|
array('id' => '2', 'tag' => 'tag2', 'created' => '2007-03-18 12:24:23', 'updated' => '2007-03-18 12:26:31')
|
||||||
|
),
|
||||||
|
'Deep' => array(
|
||||||
|
'Nesting' => array(
|
||||||
|
'test' => array(
|
||||||
|
1 => 'foo',
|
||||||
|
2 => array(
|
||||||
|
'and' => array('more' => 'stuff')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'Article' => array('id' => '3', 'user_id' => '1', 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'),
|
||||||
|
'User' => array('id' => '2', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'),
|
||||||
|
'Comment' => array(),
|
||||||
|
'Tag' => array()
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'Article' => array('id' => '3', 'user_id' => '1', 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'),
|
||||||
|
'User' => array('id' => '3', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'),
|
||||||
|
'Comment' => array(),
|
||||||
|
'Tag' => array()
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'Article' => array('id' => '3', 'user_id' => '1', 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'),
|
||||||
|
'User' => array('id' => '4', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'),
|
||||||
|
'Comment' => array(),
|
||||||
|
'Tag' => array()
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'Article' => array('id' => '3', 'user_id' => '1', 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'),
|
||||||
|
'User' => array('id' => '5', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'),
|
||||||
|
'Comment' => array(),
|
||||||
|
'Tag' => array()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$b = array('Deep' => $a[0]['Deep']);
|
||||||
|
$c = array(
|
||||||
|
array(
|
||||||
|
'a' => array(
|
||||||
|
'I' => array(
|
||||||
|
'a' => 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'a' => array(
|
||||||
|
2
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'a' => array(
|
||||||
|
'II' => array(
|
||||||
|
'a' => 3,
|
||||||
|
'III' => array(
|
||||||
|
'a' => array('foo' => 4)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$expected = array(
|
||||||
|
$c[0], $c[0]['a']['I'], $c[1], $c[2], array('a' => $c[2]['a']['II']['a']), $c[2]['a']['II']['III']
|
||||||
|
);
|
||||||
|
|
||||||
|
$expected = array(1,2,3,4,5);
|
||||||
|
$r = Set::extract('/User/id', $a);
|
||||||
|
$this->assertEqual($r, $expected);
|
||||||
|
|
||||||
|
$expected = array(array('id' => 1), array('id' => 2), array('id' => 3), array('id' => 4), array('id' => 5));
|
||||||
|
$r = Set::extract('/User/id', $a, array('flatten' => false));
|
||||||
|
$this->assertEqual($r, $expected);
|
||||||
|
|
||||||
|
$expected = array(array('test' => $a[0]['Deep']['Nesting']['test']));
|
||||||
|
$this->assertEqual(Set::extract('/Deep/Nesting/test', $a), $expected);
|
||||||
|
$this->assertEqual(Set::extract('/Deep/Nesting/test', $b), $expected);
|
||||||
|
|
||||||
|
$expected = array(array('test' => $a[0]['Deep']['Nesting']['test']));
|
||||||
|
$r = Set::extract('/Deep/Nesting/test/1/..', $a);
|
||||||
|
$this->assertEqual($r, $expected);
|
||||||
|
|
||||||
|
$expected = array(array('test' => $a[0]['Deep']['Nesting']['test']));
|
||||||
|
$r = Set::extract('/Deep/Nesting/test/2/and/../..', $a);
|
||||||
|
$this->assertEqual($r, $expected);
|
||||||
|
|
||||||
|
$expected = array(array('test' => $a[0]['Deep']['Nesting']['test']));
|
||||||
|
$r = Set::extract('/Deep/Nesting/test/2/../../../Nesting/test/2/..', $a);
|
||||||
|
$this->assertEqual($r, $expected);
|
||||||
|
|
||||||
|
$expected = array(2);
|
||||||
|
$r = Set::extract('/User[2]/id', $a);
|
||||||
|
$this->assertEqual($r, $expected);
|
||||||
|
|
||||||
|
$expected = array(4, 5);
|
||||||
|
$r = Set::extract('/User[id>3]/id', $a);
|
||||||
|
$this->assertEqual($r, $expected);
|
||||||
|
|
||||||
|
$expected = array(2, 3);
|
||||||
|
$r = Set::extract('/User[id>1][id<=3]/id', $a);
|
||||||
|
$this->assertEqual($r, $expected);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* undocumented function
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @author Felix
|
||||||
|
*/
|
||||||
|
|
||||||
|
function testMatches() {
|
||||||
|
$a = array(
|
||||||
|
array('Article' => array('id' => 1, 'title' => 'Article 1')),
|
||||||
|
array('Article' => array('id' => 2, 'title' => 'Article 2')),
|
||||||
|
array('Article' => array('id' => 3, 'title' => 'Article 3')));
|
||||||
|
|
||||||
|
$this->assertTrue(Set::matches(array('id=2'), $a[1]['Article']));
|
||||||
|
$this->assertFalse(Set::matches(array('id>2'), $a[1]['Article']));
|
||||||
|
$this->assertTrue(Set::matches(array('id>=2'), $a[1]['Article']));
|
||||||
|
$this->assertTrue(Set::matches(array('id>1'), $a[1]['Article']));
|
||||||
|
$this->assertTrue(Set::matches(array('id>1', 'id<3', 'id!=0'), $a[1]['Article']));
|
||||||
|
|
||||||
|
$this->assertTrue(Set::matches(array('3'), null, 3));
|
||||||
|
$this->assertTrue(Set::matches(array('5'), null, 5));
|
||||||
|
|
||||||
|
$this->assertTrue(Set::matches(array('id'), $a[1]['Article']));
|
||||||
|
$this->assertTrue(Set::matches(array('id', 'title'), $a[1]['Article']));
|
||||||
|
$this->assertFalse(Set::matches(array('non-existant'), $a[1]['Article']));
|
||||||
|
|
||||||
|
$this->assertTrue(Set::matches('/Article[id=2]', $a));
|
||||||
|
$this->assertFalse(Set::matches('/Article[id=4]', $a));
|
||||||
|
}
|
||||||
|
|
||||||
|
function testClassicExtract() {
|
||||||
$a = array(
|
$a = array(
|
||||||
array('Article' => array('id' => 1, 'title' => 'Article 1')),
|
array('Article' => array('id' => 1, 'title' => 'Article 1')),
|
||||||
array('Article' => array('id' => 2, 'title' => 'Article 2')),
|
array('Article' => array('id' => 2, 'title' => 'Article 2')),
|
||||||
|
|
Loading…
Add table
Reference in a new issue