mirror of
https://github.com/processwire/processwire.git
synced 2025-08-13 18:24:57 +02:00
Add find() and findOne() methods to WireInputData class, enabling $input->post->find(), $input->get->find(), etc., supporting both wildcards and regex patterns
This commit is contained in:
@@ -189,6 +189,144 @@ class WireInputData extends Wire implements \ArrayAccess, \IteratorAggregate, \C
|
||||
return $this->__get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find one input var that matches given pattern in name (or optionally value)
|
||||
*
|
||||
* @param string $pattern Wildcard string or PCRE regular expression
|
||||
* @param array|int|string $options
|
||||
* - `type` (string): Specify "value" to match input value (rather input name), OR prefix pattern with "value=".
|
||||
* - `sanitizer` (string): Name of sanitizer to run values through (default='', none)
|
||||
* - `arrays` (bool): Also find on input varibles that are arrays? (default=false)
|
||||
* @return string|int|float|array|null $value Returns value if found or null if not.
|
||||
* @since 3.0.163
|
||||
*
|
||||
*/
|
||||
public function findOne($pattern, $options = array()) {
|
||||
if(!strlen($pattern)) return null;
|
||||
if(ctype_alnum(str_replace(array('_', '-', '.'), '', $pattern))) return $this->__get($pattern);
|
||||
$options['limit'] = 1;
|
||||
$value = $this->find($pattern, array_merge($options, $options));
|
||||
return array_shift($value); // returns null if empty
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all input vars that match given pattern in name (or optionally value)
|
||||
*
|
||||
* ~~~~~
|
||||
* // find all input vars having name beginning with "title_" (i.e. title_en, title_de, title_es)
|
||||
* $values = $input->post->find('title_*');
|
||||
*
|
||||
* // find all input vars having name with "title" anywhere in it (i.e. title, subtitle, titles, title_de)
|
||||
* $values = $input->post->find('*title*');
|
||||
*
|
||||
* // find all input vars having value with the term "wire" anywhere, regardless of case
|
||||
* $values = $input->post->find('/wire/i', [ 'type' => 'value' ]);
|
||||
*
|
||||
* // example of result from above find operation:
|
||||
* $values = [
|
||||
* 'title' => 'ProcessWire CMS',
|
||||
* 'subtitle' => 'Have plenty of caffeine to make sure you are wired',
|
||||
* 'sidebar' => 'Learn how to rewire a flux capacitor...',
|
||||
* 'summary' => 'All about the $wire API variable',
|
||||
* ];
|
||||
* ~~~~~
|
||||
*
|
||||
* @param string $pattern Wildcard string or PCRE regular expression
|
||||
* @param array $options
|
||||
* - `type` (string): Specify "value" to match input value (rather input name), OR prefix pattern with "value=".
|
||||
* - `limit` (int): Maximum number of items to return (default=0, no limit)
|
||||
* - `sanitizer` (string): Name of sanitizer to run values through (default='', none)
|
||||
* - `arrays` (bool): Also find on input varibles that are arrays? (default=false)
|
||||
* @return array Returns associative array of values `[ name => value ]` if found, or empty array if none found.
|
||||
* @since 3.0.163
|
||||
*
|
||||
*/
|
||||
public function find($pattern, array $options = array()) {
|
||||
|
||||
$defaults = array(
|
||||
'type' => 'name', // match on 'name' or 'value' (default='name')
|
||||
'limit' => 0, // max allowed matches in return value
|
||||
'values' => $this, // use these values rather than those from this input class
|
||||
'sanitizer' => '', // sanitizer name to apply found values
|
||||
'arrays' => false, // also find on input vars that are arrays?
|
||||
);
|
||||
|
||||
if(!strlen($pattern)) return array();
|
||||
|
||||
$options = array_merge($defaults, $options);
|
||||
$sanitizer = $this->wire('sanitizer'); /** @var Sanitizer $sanitizer */
|
||||
$isRE = in_array($pattern[0], array('/', '!', '%', '#', '@'));
|
||||
$items = array();
|
||||
$count = 0;
|
||||
$type = $options['type'];
|
||||
$tests = array();
|
||||
|
||||
if(!strlen($pattern)) return array();
|
||||
|
||||
if(strpos($pattern, '=')) {
|
||||
// pattern indicates "value=pattern" or "name=pattern"
|
||||
list($type, $pattern) = explode('=', $pattern, 2);
|
||||
}
|
||||
|
||||
if(!$isRE && strpos($pattern, '*') !== false) {
|
||||
// wildcard, convert to regex
|
||||
$a = explode('*', $pattern);
|
||||
foreach($a as $k => $v) {
|
||||
if(!strlen($v)) continue;
|
||||
$a[$k] = preg_quote($v);
|
||||
$tests[] = $v;
|
||||
}
|
||||
$isRE = true;
|
||||
$pattern = '/^' . implode('.*', $a) . '$/';
|
||||
}
|
||||
|
||||
if(!count($tests)) $tests = false;
|
||||
|
||||
foreach($options['values'] as $name => $value) {
|
||||
|
||||
if($options['limit'] && $count >= $options['limit']) break;
|
||||
|
||||
$isArray = is_array($value);
|
||||
|
||||
if($isArray && !$options['arrays']) {
|
||||
continue;
|
||||
} else if($isArray && $type === 'value') {
|
||||
$v = $this->find($pattern, array_merge($options, array('values' => $value)));
|
||||
if(count($v)) list($items[$name], $count) = array($v, $count + 1);
|
||||
continue;
|
||||
} else if($type === 'value') {
|
||||
$match = $value;
|
||||
} else {
|
||||
$match = $name;
|
||||
}
|
||||
|
||||
if($tests) {
|
||||
// tests to confirm a preg_match is necessary (wildcard mode only)
|
||||
$passes = true;
|
||||
foreach($tests as $test) {
|
||||
$passes = strpos($match, $test) !== false;
|
||||
if(!$passes) break;
|
||||
}
|
||||
if(!$passes) continue;
|
||||
}
|
||||
|
||||
if($isRE) {
|
||||
if(!preg_match($pattern, $match)) continue;
|
||||
} else {
|
||||
if(strpos($match, $pattern) === false) continue;
|
||||
}
|
||||
|
||||
if($options['sanitizer']) {
|
||||
$value = $sanitizer->sanitize($value, $options['sanitizer']);
|
||||
}
|
||||
|
||||
$items[$name] = $value;
|
||||
$count++;
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean an array of data
|
||||
*
|
||||
@@ -282,12 +420,25 @@ class WireInputData extends Wire implements \ArrayAccess, \IteratorAggregate, \C
|
||||
public function count() {
|
||||
return count($this->data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove a value from input
|
||||
*
|
||||
* @param string $key Name of input variable to remove value for
|
||||
* @return $this
|
||||
*
|
||||
*/
|
||||
public function remove($key) {
|
||||
$this->offsetUnset($key);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all values from input
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
*/
|
||||
public function removeAll() {
|
||||
$this->data = array();
|
||||
$this->lazy = false;
|
||||
@@ -303,16 +454,30 @@ class WireInputData extends Wire implements \ArrayAccess, \IteratorAggregate, \C
|
||||
$this->offsetUnset($key);
|
||||
}
|
||||
|
||||
public function queryString($overrides = array()) {
|
||||
return http_build_query(array_merge($this->getArray(), $overrides));
|
||||
/**
|
||||
* Return a query string of all input values
|
||||
*
|
||||
* Please note returned query string contains non-sanitized/non-validated variables, so this method
|
||||
* should only be used for specific cases where all input is known to be safe/valid. If that is not
|
||||
* an option then use PHP’s `http_build_query()` function on your own with known safe/valid values.
|
||||
*
|
||||
* #pw-internal
|
||||
*
|
||||
* @param array $overrides Associative array of [ name => value ] containing values to override/replace
|
||||
* @param string $separator String to separate values with, i.e. '&' or '&' (default='&')
|
||||
* @return string
|
||||
* @since 3.0.163
|
||||
*
|
||||
*/
|
||||
public function queryString($overrides = array(), $separator = '&') {
|
||||
return http_build_query(array_merge($this->getArray(), $overrides), '', $separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps to Sanitizer functions
|
||||
*
|
||||
* @param $method
|
||||
* @param $arguments
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @return string|int|array|float|null Returns null when input variable does not exist
|
||||
* @throws WireException
|
||||
*
|
||||
|
Reference in New Issue
Block a user