1
0
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:
Ryan Cramer
2020-07-23 16:54:07 -04:00
parent c6c639c0db
commit 8c5ed3e0b1

View File

@@ -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 PHPs `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
*