Implements partial support for XPath 2.0.
public static extract ( array $data, string $path = null, array $options = [] ) : array | ||
$data | array | An array of data to extract from. |
$path | string | An absolute XPath 2.0 path. Only absolute paths starting with a single slash are supported right now. Implemented selectors: - `'/User/id'`: Similar to the classic {n}.User.id. - `'/User[2]/name'`: Selects the name of the second User. - `'/User[id>2]'`: Selects all Users with an id > 2. - `'/User[id>2][<5]'`: Selects all Users with an id > 2 but < 5. - `'/Post/Comment[author_name=John]/../name'`: Selects the name of all posts that have at least one comment written by John. - `'/Posts[name]'`: Selects all Posts that have a `'name'` key. - `'/Comment/.[1]'`: Selects the contents of the first comment. - `'/Comment/.[:last]'`: Selects the last comment. - `'/Comment/.[:first]'`: Selects the first comment. - `'/Comment[text=/lithium/i]`': Selects the all comments that have a text matching the regex `/lithium/i`. - `'/Comment/@*'`: Selects all key names of all comments. |
$options | array | Currently only supports `'flatten'` which can be disabled for higher XPath-ness. |
return | array | An array of matched items. |
/** * returns rendered content * * @param string $content input content * @param string $data field to retrieve from configuration * @param array $options an array with additional options * @return string content as given * @filter */ public function get($content, $data = null, array $options = array()) { $defaults = array('default' => array(), 'flat' => false); $options += $defaults; $params = compact('content', 'data', 'options'); return $this->_filter(__METHOD__, $params, function ($self, $params) { extract($params); try { $config = NeonFormatter::decode($content); } catch (NeonException $e) { return $options['default']; } catch (Exception $e) { return $options['default']; } if (!empty($data) && is_scalar($data)) { if (array_key_exists($data, (array) $config)) { return $config[$data]; } } if ($data) { $data = '/' . str_replace('.', '/', $data) . '/.'; $result = current(Set::extract((array) $config, $data)); return !empty($result) ? $result : null; } return $options['flat'] ? Set::flatten($config) : $config; }); }
/** * returns rendered content * * @param string $content input content * @param string $data field to retrieve from configuration * @param array $options an array with additional options * @return string content as given * @filter */ public function get($content, $data = null, array $options = array()) { $defaults = array('default' => null, 'flat' => false); $options += $defaults; $params = compact('content', 'data', 'options'); return $this->_filter(__METHOD__, $params, function ($self, $params) { extract($params); try { $config = IniFormat::parse($content); } catch (IniFormatException $e) { return $options['default']; } catch (Exception $e) { return $options['default']; } if (empty($data)) { return $options['flat'] ? Set::flatten($config) : $config; } if (is_scalar($data) && isset($config[$data])) { return $config[$data]; } $data = '/' . str_replace('.', '/', (string) $data) . '/.'; $result = current(Set::extract((array) $config, $data)); if (!empty($result)) { return $result; } return $options['default']; }); }
/** * Checks a single-use hash key against the session token that generated it, using * a cryptographically-secure verification method. Accepts either the request key as a string, * or a `Request` object with a `$data` property containing a `['security']['token']` key. * * For example, the following two controller code samples are equivalent: * * {{{ * $key = $this->request->data['security']['token']; * * if (!RequestToken::check($key)) { * // Handle invalid request... * } * }}} * * {{{ * if (!RequestToken::check($this->request)) { * // Handle invalid request... * } * }}} * * @param mixed $key Either the actual key as a string, or a `Request` object containing the * key. * @param array $options The options to use when matching the key to the token: * - `'sessionKey'` _string_: The key used when reading the token from the session. * @return boolean Returns `true` if the hash key is a cryptographic match to the stored * session token. Returns `false` on failure, which indicates a forged request attempt. */ public static function check($key, array $options = array()) { $defaults = array('sessionKey' => 'security.token'); $options += $defaults; $session = static::$_classes['session']; if (is_object($key) && isset($key->data)) { $result = Set::extract($key->data, '/security/token'); $key = $result ? $result[0] : null; } return Password::check($session::read($options['sessionKey']), (string) $key); }
public function testInsertAndRemoveWithFunkyKeys() { $set = Set::insert(array(), 'Session Test', "test"); $result = Set::extract($set, '/Session Test'); $this->assertEqual($result, array('test')); $set = Set::remove($set, 'Session Test'); $this->assertFalse(Set::check($set, 'Session Test')); $this->assertTrue($set = Set::insert(array(), 'Session Test.Test Case', "test")); $this->assertTrue(Set::check($set, 'Session Test.Test Case')); }
public function testNodeWithNonStrictMode() { Fixtures::save('db'); extract($this->_models); $result = Set::extract(Aco::node('root/printers/refill/unexisting', false), '/id'); $expected = ['9', '6', '1']; $this->assertEqual($expected, $result); }
/** * Delete a value from the cookie store. * * @param string $key The key to be deleted from the cookie store. * @param array $options Options array. * @return \Closure Function returning boolean `true` on successful delete, `false` otherwise. */ public function delete($key, array $options = array()) { $config = $this->_config; $cookieClass = get_called_class(); return function ($self, $params) use(&$config, $cookieClass) { $key = $params['key']; $path = '/' . str_replace('.', '/', $config['name'] . '.' . $key) . '/.'; $cookies = current(Set::extract($_COOKIE, $path)); if (is_array($cookies)) { $cookies = array_keys(Set::flatten($cookies)); foreach ($cookies as &$name) { $name = $key . '.' . $name; } } else { $cookies = array($key); } foreach ($cookies as &$name) { $name = $cookieClass::keyFormat($name, $config); $result = setcookie($name, "", 1, $config['path'], $config['domain'], $config['secure'], $config['httponly']); if (!$result) { throw new RuntimeException("There was an error deleting {$name} cookie."); } } return true; }; }
/** * Filter for connection->read to take alternate relations and call a batch ::find on appropiate relation */ protected static function _connectionFilters() { $connection = static::connection(); if (!isset($connection->_hasRelationFilter) || $connection->_hasRelationFilter == false) { $connection->_hasRelationFilter = true; $connection->applyFilter('read', function ($self, $params, $chain) { $data = $chain->next($self, $params, $chain); // check to see if there are any alternateRelations if (!empty($data) && is_object($data) && !empty($params) && isset($params['options']) && isset($params['options']['alternateWith']) && !empty($params['options']['alternateWith'])) { $alternateRelations = $params['options']['model']::relations(null, 'alternate'); if (!empty($alternateRelations)) { if (method_exists($data, 'map')) { $records = $data->to('array'); } else { $records[] = $data->to('array'); } foreach ($params['options']['alternateWith'] as $key => $val) { // TODO add support for 'Relation' => array(options) if (is_int($key)) { $relationKey = $val; } else { $relationKey = $key; } $relation = null; if (isset($alternateRelations[$relationKey])) { // get options from relationship $relation = $alternateRelations[$relationKey]->data(); if (!is_int($key) && !empty($val)) { $relation = array_merge($relation, $val); } } if (!empty($relation)) { $relationModel = $relation['to']; $searchAssociations = array(); $searchValues = array(); $keys = array_keys($relation['key']); $from = (string) array_shift($keys); $fromArray = explode('.', $from); $to = (string) $relation['key'][$from]; if (!empty($relation['fieldName'])) { $field = $relation['fieldName']; } else { $field = $class; } // grab all ids from ids to create one batch query foreach ($records as $k => $record) { $searchValue = Set::extract($record, '/' . str_replace('.', '/', $from)); $lastKey = array_slice($fromArray, -1, 1); $lastKey = $lastKey[0]; if (!empty($searchValue) && (!is_array($searchValue[0]) || is_array($searchValue[0]) && !isset($searchValue[0][$lastKey]))) { if (!is_array($searchValue)) { $searchValue = array($searchValue); } $searchValues = array_merge($searchValues, $searchValue); $searchAssociations[$k] = $searchValue; } else { $searchAssociations[$k] = null; } } // if we have at least one id if (!empty($searchValues)) { $searchValues = array_unique($searchValues); $relation['conditions'][$to] = $searchValues; $unsetSearchKey = false; if (is_array($relation['fields'])) { if (!in_array($to, $relation['fields'])) { $relation['fields'][] = $to; $unsetSearchKey = true; } } else { $relation['fields'] = null; } $relationalData = $relationModel::find('all', $relation); if (!empty($relationalData)) { $results = array(); foreach ($relationalData as $item) { $ids = array(); if (is_object($item) && isset($item->{$to})) { if (method_exists($item->{$to}, 'to')) { $ids = $item->{$to}->to('array'); } else { $ids = array((string) $item->{$to}); } } foreach ($ids as $id) { if (!empty($id)) { if ($unsetSearchKey === true && isset($item->{$to})) { unset($item->{$to}); } $results[$id][] = $item; } } } } } // check to make sure we have at least one association if (!empty($searchAssociations) && isset($results)) { foreach ($searchAssociations as $itemKey => $value) { if (isset($data[$itemKey])) { // create an associationResult to hold all of this items related data $associationResult = array(); if (!is_null($value) && isset($results)) { if (is_array($value)) { // sort values to populate in the order returned by mongo result $value = array_keys(array_intersect_key($results, array_fill_keys($value, null))); foreach ($value as $searchKey) { $searchKey = (string) $searchKey; if (isset($results[$searchKey])) { $associationResult = array_merge($associationResult, $results[$searchKey]); } } // add some processing for grouping - this was added for mongo, may not be needed if (isset($relation['group'])) { $groupedResult = array(); foreach ($associationResult as $k => $result) { $comparison = array(); foreach ($relation['group'] as $group_item) { if (isset($result->{$group_item})) { $comparison[] = $result->{$group_item}; } else { $comparison[] = null; } } if (!empty($comparison)) { $groupedResult[$k] = json_encode($comparison); } } $groupedResult = array_unique($groupedResult); $associationResult = array_values(array_intersect_key($associationResult, $groupedResult)); } } } $relationConnection = $relationModel::connection(); // check to see if we have a result && if relation type is hasOne, if so shift result to one element if (count($associationResult) > 0) { if ($relation['type'] == 'hasOne') { $associationResult = array_shift($associationResult); } else { $associationResult = $relationConnection->item($relationModel, $associationResult, array('class' => 'set')); } // else if result is empty, create default empty response. hasMany defaults to connection collection & hasOne defaults to null - Same response as provided via ::find('all') vs ::find('first') } else { if ($relation['type'] == 'hasOne') { $associationResult = null; } else { $associationResult = $relationConnection->item($relationModel, array(), array('class' => 'set')); } } // finally, add the relation if (method_exists($data, 'map')) { $data[$itemKey]->{$field} = $associationResult; } else { $data->{$field} = $associationResult; } } } } } } } } return $data; }); } }
public function _parseClass($class) { $data = array(); // $methods = Inspector::methods($class, null, array('public' => false)); $methods = get_class_methods($class); $properties = array_keys(get_class_vars($class)); $ident = $class; $info = Inspector::info($ident); $info = Docblock::comment($info['comment']); $data = $this->_merge($data, array('id' => $info['description'], 'comments' => array($ident))); $this->_merge($data, array('id' => $info['text'], 'comments' => array($class))); foreach ($methods as $method) { $ident = "{$class}::{$method}()"; $info = Inspector::info($ident); $info = Docblock::comment($info['comment']); $this->_merge($data, array('id' => $info['description'], 'comments' => array($ident))); $this->_merge($data, array('id' => $info['text'], 'comments' => array($ident))); if (isset($info['tags']['return'])) { $this->_merge($data, array('id' => $info['tags']['return'], 'comments' => array($ident))); } foreach (Set::extract($info, '/tags/params/text') as $text) { $this->_merge($data, array('id' => $text, 'comments' => array($ident))); } } foreach ($properties as $property) { $ident = "{$class}::\${$property}"; $info = Inspector::info($ident); $info = Docblock::comment($info['comment']); $data = $this->_merge($data, array('id' => $info['description'], 'comments' => array($ident))); $data = $this->_merge($data, array('id' => $info['text'], 'comments' => array($ident))); } return $data; }
protected function _readEmbeddedFilter() { // filter for relations self::applyFilter('read', function ($self, $params, $chain) { $results = $chain->next($self, $params, $chain); if (isset($params['options']['with']) && !empty($params['options']['with'])) { $model = is_object($params['query']) ? $params['query']->model() : null; $relations = $model::relations(); foreach ($params['options']['with'] as $k => $name) { if (isset($relations[$name])) { $relation = $relations[$name]->data(); $relationModel = Libraries::locate('models', $relation['to']); if (!empty($relationModel) && !empty($results) && isset($relation['embedded'])) { $embedded_on = $relation['embedded']; $resultsArray = $results->to('array'); foreach ($resultsArray as $k => $result) { $relationalData = Set::extract($result, '/' . str_replace('.', '/', $embedded_on)); if (!empty($embedded_on)) { $keys = explode('.', $embedded_on); $lastKey = array_slice($keys, -1, 1); $lastKey = $lastKey[0]; $data = array(); foreach ($relationalData as $rk => $rv) { if (!empty($rv)) { if (!is_array($rv)) { $data[$rk] = $rv; } else { if (isset($rv[$lastKey]) && !empty($rv[$lastKey])) { $data[$rk] = $rv[$lastKey]; } } } } if (!empty($data)) { // TODO : Add support for conditions, fields, order, page, limit $validFields = array_fill_keys(array('with'), null); $options = array_intersect_key($relation, $validFields); $options['data'] = $data; if ($relation['type'] == 'hasMany') { $type = 'all'; } else { $type = 'first'; } $relationResult = $relationModel::find($type, $options); } else { if ($relation['type'] == 'hasMany') { $relationResult = $self->item($relationModel, array(), array('class' => 'set')); } else { $relationResult = null; } } // if fieldName === true, use the default lithium fieldName. // if fieldName != relationName, then it was manually set, so use it // else, just use the embedded key $relationName = $relation['type'] == 'hasOne' ? Inflector::pluralize($relation['name']) : $relation['name']; if ($relation['fieldName'] === true) { $relation['fieldName'] = lcfirst($relationName); $keys = explode('.', $relation['fieldName']); } else { if ($relation['fieldName'] != lcfirst($relationName)) { $keys = explode('.', $relation['fieldName']); } } $ref = $results[$k]; foreach ($keys as $k => $key) { if (!isset($ref->{$key})) { $ref->{$key} = $self->item(null, array(), array('class' => 'entity')); } if (count($keys) - 1 == $k) { $ref->{$key} = $relationResult; } else { $ref = $ref->{$key}; } } } } } } } } return $results; }); }
/** * Get all permission access * * @param string $requester The requesting identifier (Aro). * @param string $controlled The controlled identifier (Aco). */ public static function get($requester, $controlled) { $self = static::_object(); $aro = $self->_classes['aro']; $aco = $self->_classes['aco']; if (!(($aroNodes = $aro::node($requester, false)) && ($acoNodes = $aco::node($controlled, false)))) { return false; } $privileges = []; $aro_id = key(static::relations('Aro')->key()); $aco_id = key(static::relations('Aco')->key()); $left = $aco::actsAs('Tree', true, 'left'); $ids = Set::extract($acoNodes, '/' . $aco::meta('key')); foreach ($aroNodes as $node) { $id = $node[$aro::meta('key')]; if ($datas = static::_permQuery($id, $ids, $aro_id, $aco_id, $left)) { foreach ($datas as $data) { $privileges = $privileges + (array) json_decode($data['privileges'], true); } } } return $privileges; }