I have a project where I need some basic boolean search in pure PHP. It means I have plain strings I want to offer some simple boolean search on them. No database or other indexing engine is involved, so please don't refer to MySQL boolean search or lucene.
At the end something like the following code should print contains and not found.
$search = 'foo -bar "must have" -"must not have"';
$contentFound = 'This is some foo text you must have.';
$contentNotFound = 'This is some bar text you must have.';
if ($this->booleanSearch($contentFound, $search)) {
echo 'contains';
} else {
echo 'not found';
}
if ($this->booleanSearch($contentNotFound, $search)) {
echo 'contains';
} else {
echo 'not found';
}
For a simple implementation you could just split the criteria (taking into account the quotes) and iterate over each criterion to see whether it matches or not:
function booleanSearch($content, $search) {
$criteria = str_getcsv($search, ' ');
while ($criteria) {
$not = false;
$q = array_shift($criteria);
if (substr($q, 0, 2) === '-"') {
$not = true;
while (substr($q, -1) != '"') {
$q .= " " . array_shift($criteria);
}
$q = substr($q, 2, -1);
}
else if (substr($q, 0, 1) === '-' && strpos($q, ' ') === false) {
$not = true;
$q = substr($q, 1);
}
$found = strpos($content, $q) !== false;
if ($found === $not) {
return false;
}
}
return true;
}
Related
So, I want to check the users-input, if it contains some of these characters:
" ' < >
I hope someone can show me a better way with less code
Thanks!
I used preg_match, but i just managed it with 4 nested if's.
/*Checks if the given value is valid*/
private function checkValidInput($input)
{
/*If there is no " */
if(preg_match('/"/', $input) == false)
{
/*If there is no ' */
if(preg_match("/'/", $input) == false)
{
/*If there is no <*/
if(preg_match("/</", $input) == false)
{
/*If there is no >*/
if(preg_match("/>/", $input) == false)
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
else
{
return false;
}
}
else
{
return false;
}
}
You could create a regex class
preg_match('#["\'<>]#', $input);
Edit:
If you need to check for all characters then use strpos() with for loop
function checkInput($val) {
$contains = true;
$required = "<>a";
for($i = 0, $count = strlen($required); $i < $count ; ++$i) {
$contains = $contains && false !== strpos($val, $required[$i]);
}
return $contains;
}
var_dump(checkInput('abcd<>a')); // true
var_dump(checkInput('abcd>a')); // false, doesn't contain <
I would like to search for multiple words within a string. I currently have the following condition. This seems very clunky and ugly. Is there a nicer way of doing it?
if (isset($myVar) && $myVar && strpos($myVar, 'foo') === false && strpos($myVar, 'bar') === false && strpos($myVar, 'steve') === false && strpos($myVar, 'one') === false && strpos($myVar, 'jobs') === false && strpos($myVar, 'dog') === false && strpos($myVar, 'blue') === false && strpos($myVar, 'cloud') === false && strpos($myVar, 'apple') === false)
{
// words do not exists in string
}
else
{
// words do not exists in string
}
Just like #j08691 said, build an array of the search terms then loop through it.
<?php
$words = array ( 'foo', 'bar', 'steve' );
$x=0;
do {
$found = strpos($myVar, $words[$x]);
$x++;
} while ($found !== false);
if ($found !== false) {
echo 'has all';
}
else {
echo 'words do not exists in string';
}
Strategy 1, using a loop:
You can iterate over an array containing all the words, and do a strpos one by one. This Is essentially the same as your current solution, but is less tedious if you have a lot of words and way easier to maintain and extend with more words.
$words = array('foo', 'bar', 'steve', 'one', 'jobs', 'dog', 'blue', 'cloud', 'apple');
$match = false;
foreach ($words as $word) {
if (strpos($myVar, $word) !== false) {
$match = true;
break;
}
}
if ($match) {
// ...
} else {
// ...
}
You could break this out to a generic function for reuse
function str_contains($haystack, $needles, $mode = 'or') {
foreach ($words as $word) {
if (strpos($myVar, $word) !== false) {
if ($mode == 'or') {
return true;
}
} else {
if ($mode == 'and') {
return false;
}
}
}
return $mode == 'or' ? false : true;
}
$words = array(...);
if (str_contains($myVar, $words)) {
// one of your words are included
} else {
// none of your words are included
}
if (str_contains($myVar, $words, 'and')) {
// all of your words are included
} else {
// at least one of your words are not included
}
Strategy 2, using a regular expression:
Alternatively, you could create a regular expression and do this with preg_match
$words = array('foo', 'bar', 'steve', 'one', 'jobs', 'dog', 'blue', 'cloud', 'apple');
if (preg_match('/(' . implode('|', $words) . ')/', $myVar)) {
// match
} else {
// no match
}
you can use the preg_match() and pass the words as literal regex, like this
if (isset($myVar) && $myVar && (preg_mach("(foo|bar|fooo)", $myVar) === 1)) {
//do somthing
} else {
//do something else
}
You can use preg_match :
if(preg_match('(bad|naughty)', $data) === 1) {
//TO DO
}
I hope it help.
You can use the array_intersect() function to check if some content from an array is found within another array.
$words = ['foo', 'bar', 'steve'];
if (array_intersect($words, explode(' ', $myVar))) {
// Exists
}
$words_to_search = ["wordA", "wordB", "wordC"];
foreach($words_to_search as $word){
if(strpos($myVar, "$word"){
//Exists
} else{
//Doesn't
}
}
Using PHP write an anagram function? It should be handling different phrases and return boolean result.
Usage:
$pharse1 = 'ball';
$pharse2 = 'lbal';
if(is_anagram($pharse1,$pharse2)){
echo $pharse1 .' & '. $pharse2 . ' are anagram';
}else{
echo $pharse1 .' & '. $pharse2 . ' not anagram';
}
There's simpler way
function is_anagram($a, $b) {
return(count_chars($a, 1) == count_chars($b, 1));
}
example:
$a = 'argentino';
$b = 'ignorante';
echo is_anagram($a,$b); // output: 1
$a = 'batman';
$b = 'barman';
echo is_anagram($a,$b); // output (empty):
function is_anagram($pharse1,$pharse2){
$status = false;
if($pharse1 && $pharse2){
$pharse1=strtolower(str_replace(" ","", $pharse1));
$pharse2=strtolower(str_replace(" ","", $pharse2));
$pharse1 = str_split($pharse1);
$pharse2 = str_split($pharse2);
sort($pharse1);
sort($pharse2);
if($pharse1 === $pharse2){
$status = true;
}
}
return $status;
}
function check_anagram($str1, $str2) {
if (count_chars($str1, 1) == count_chars($str2, 1)) {
return "This '" . $str1 . "', '" . $str2 . "' are Anagram";
}
else {
return "This two strings are not anagram";
}
}
ECHO check_anagram('education', 'ducatione');
I don't see any answers which have addressed the fact that capital letters are different characters than lowercase to count_chars()
if (isAnagram('Polo','pool')) {
print "Is anagram";
} else {
print "This is not an anagram";
}
function isAnagram($string1, $string2)
{
// quick check, eliminate obvious mismatches quickly
if (strlen($string1) != strlen($string2)) {
return false;
}
// Handle uppercase to lowercase comparisons
$array1 = count_chars(strtolower($string1));
$array2 = count_chars(strtolower($string2));
// Check if
if (!empty(array_diff_assoc($array2, $array1))) {
return false;
}
if (!empty(array_diff_assoc($array1, $array2))) {
return false;
}
return true;
}
here is my variant :
public function is_anagram($wrd_1, $wrd_2)
{
$wrd_1 = str_split ( strtolower ( utf8_encode($wrd_1) ) );
$wrd_2 = str_split( strtolower ( utf8_encode($wrd_2) ) );
if ( count($wrd_1)!= count($wrd_2) ) return false;
if ( count( array_diff ( $wrd_1 ,$wrd_2) ) > 0 ) return false;
return true;
}
Heheh little large but work as well :)
public static function areStringsAnagrams($a, $b)
{
//throw new Exception('Waiting to be implemented.');
$a = str_split($a);
$test = array();
$compare = array();
foreach ($a as $key) {
if (!in_array($key, $test)) {
array_push($test, $key);
$compare[$key] = 1;
} else {
$compare[$key] += 1;
}
}
foreach ($compare as $key => $value) {
if ($value !== substr_count($b, $key)) {
return false;
}
}
return true;
}
let's say I have 2 set of string to check.
$string = 12345;
$string2 = 15000;
//string must contain 1,2,3,4,5 to be returned true
if(preg_match('[1-5]',$string) {
return true;
} else {
return false;}
This code works for $string but not for $string2. It returns true too with $string2.
Please help!
If string must contain 1, 2, 3, 4 and 5, then you should use regex pattern
/^(?=.*1)(?=.*2)(?=.*3)(?=.*4)(?=.*5).*/
which can be further optimize... for example:
/^(?=.*1)(?=.*2)(?=.*3)(?=.*4).*5/
If no other characters are allowed, then you should use regex pattern
/^(?=.*1)(?=.*2)(?=.*3)(?=.*4)(?=.*5)[1-5]*$/
You can check this with strpos as well:
<?php
function str_contains_all($string, $searchValues, $caseSensitive = false) {
if (!is_array($searchValues)) {
$searchValues = (string)$searchValues;
$searchValuesNew = array();
for ($i = 0; $i < strlen($searchValues); $i++) {
$searchValuesNew[] = $searchValues[$i];
}
$searchValues = $searchValuesNew;
}
$searchFunction = ($caseSensitive ? 'strpos' : 'stripos');
foreach ($searchValues as $searchValue) {
if ($searchFunction($string, (string)$searchValue) === false) {
return false;
}
}
return true;
}
?>
Use:
<?php
$string = 12345;
$string2 = 15000;
if (str_contains_all($string, 12345)) {
echo 'Y';
if (str_contains_all($string2, 12345)) {
echo 'Y';
} else {
echo 'N';
}
} else {
echo 'N';
}
?>
Which outputs:
YN
DEMO
I'm writing code to recursively replace predefined variables from inside a given string. The variables are prefixed with the character '%'. Input strings that start with '^' are to be evaluated.
For instance, assuming an array of variables such as:
$vars['a'] = 'This is a string';
$vars['b'] = '123';
$vars['d'] = '%c'; // Note that $vars['c'] has not been defined
$vars['e'] = '^5 + %d';
$vars['f'] = '^11 + %e + %b*2';
$vars['g'] = '^date(\'l\')';
$vars['h'] = 'Today is %g.';
$vars['input_digits'] = '*****';
$vars['code'] = '%input_digits';
The following code would result in:
a) $str = '^1 + %c';
$rc = _expand_variables($str, $vars);
// Result: $rc == 1
b) $str = '^%a != NULL';
$rc = _expand_variables($str, $vars);
// Result: $rc == 1
c) $str = '^3+%f + 3';
$rc = _expand_variables($str, $vars);
// Result: $rc == 262
d) $str = '%h';
$rc = _expand_variables($str, $vars);
// Result: $rc == 'Today is Monday'
e) $str = 'Your code is: %code';
$rc = _expand_variables($str, $vars);
// Result: $rc == 'Your code is: *****'
Any suggestions on how to do that? I've spent many days trying to do this, but only achieved partial success. Unfortunately, my last attempt managed to generate a 'segmentation fault'!!
Help would be much appreciated!
Note that there is no check against circular inclusion, which would simply lead to an infinite loop. (Example: $vars['s'] = '%s'; ..) So make sure your data is free of such constructs.
The commented code
// if(!is_numeric($expanded) || (substr($expanded.'',0,1)==='0'
// && strpos($expanded.'', '.')===false)) {
..
// }
can be used or skipped. If it is skipped, any replacement is quoted, if the string $str will be evaluated later on! But since PHP automatically converts strings to numbers (or should I say it tries to do so??) skipping the code should not lead to any problems.
Note that boolean values are not supported! (Also there is no automatic conversion done by PHP, that converts strings like 'true' or 'false' to the appropriate boolean values!)
<?
$vars['a'] = 'This is a string';
$vars['b'] = '123';
$vars['d'] = '%c';
$vars['e'] = '^5 + %d';
$vars['f'] = '^11 + %e + %b*2';
$vars['g'] = '^date(\'l\')';
$vars['h'] = 'Today is %g.';
$vars['i'] = 'Zip: %j';
$vars['j'] = '01234';
$vars['input_digits'] = '*****';
$vars['code'] = '%input_digits';
function expand($str, $vars) {
$regex = '/\%(\w+)/';
$eval = substr($str, 0, 1) == '^';
$res = preg_replace_callback($regex, function($matches) use ($eval, $vars) {
if(isset($vars[$matches[1]])) {
$expanded = expand($vars[$matches[1]], $vars);
if($eval) {
// Special handling since $str is going to be evaluated ..
// if(!is_numeric($expanded) || (substr($expanded.'',0,1)==='0'
// && strpos($expanded.'', '.')===false)) {
$expanded = "'$expanded'";
// }
}
return $expanded;
} else {
// Variable does not exist in $vars array
if($eval) {
return 'null';
}
return $matches[0];
}
}, $str);
if($eval) {
ob_start();
$expr = substr($res, 1);
if(eval('$res = ' . $expr . ';')===false) {
ob_end_clean();
die('Not a correct PHP-Expression: '.$expr);
}
ob_end_clean();
}
return $res;
}
echo expand('^1 + %c',$vars);
echo '<br/>';
echo expand('^%a != NULL',$vars);
echo '<br/>';
echo expand('^3+%f + 3',$vars);
echo '<br/>';
echo expand('%h',$vars);
echo '<br/>';
echo expand('Your code is: %code',$vars);
echo '<br/>';
echo expand('Some Info: %i',$vars);
?>
The above code assumes PHP 5.3 since it uses a closure.
Output:
1
1
268
Today is Tuesday.
Your code is: *****
Some Info: Zip: 01234
For PHP < 5.3 the following adapted code can be used:
function expand2($str, $vars) {
$regex = '/\%(\w+)/';
$eval = substr($str, 0, 1) == '^';
$res = preg_replace_callback($regex, array(new Helper($vars, $eval),'callback'), $str);
if($eval) {
ob_start();
$expr = substr($res, 1);
if(eval('$res = ' . $expr . ';')===false) {
ob_end_clean();
die('Not a correct PHP-Expression: '.$expr);
}
ob_end_clean();
}
return $res;
}
class Helper {
var $vars;
var $eval;
function Helper($vars,$eval) {
$this->vars = $vars;
$this->eval = $eval;
}
function callback($matches) {
if(isset($this->vars[$matches[1]])) {
$expanded = expand($this->vars[$matches[1]], $this->vars);
if($this->eval) {
// Special handling since $str is going to be evaluated ..
if(!is_numeric($expanded) || (substr($expanded . '', 0, 1)==='0'
&& strpos($expanded . '', '.')===false)) {
$expanded = "'$expanded'";
}
}
return $expanded;
} else {
// Variable does not exist in $vars array
if($this->eval) {
return 'null';
}
return $matches[0];
}
}
}
I now have written an evaluator for your code, which addresses the circular reference problem, too.
Use:
$expression = new Evaluator($vars);
$vars['a'] = 'This is a string';
// ...
$vars['circular'] = '%ralucric';
$vars['ralucric'] = '%circular';
echo $expression->evaluate('%circular');
I use a $this->stack to handle circular references. (No idea what a stack actually is, I simply named it so ^^)
class Evaluator {
private $vars;
private $stack = array();
private $inEval = false;
public function __construct(&$vars) {
$this->vars =& $vars;
}
public function evaluate($str) {
// empty string
if (!isset($str[0])) {
return '';
}
if ($str[0] == '^') {
$this->inEval = true;
ob_start();
eval('$str = ' . preg_replace_callback('#%(\w+)#', array($this, '_replace'), substr($str, 1)) . ';');
if ($error = ob_get_clean()) {
throw new LogicException('Eval code failed: '.$error);
}
$this->inEval = false;
}
else {
$str = preg_replace_callback('#%(\w+)#', array($this, '_replace'), $str);
}
return $str;
}
private function _replace(&$matches) {
if (!isset($this->vars[$matches[1]])) {
return $this->inEval ? 'null' : '';
}
if (isset($this->stack[$matches[1]])) {
throw new LogicException('Circular Reference detected!');
}
$this->stack[$matches[1]] = true;
$return = $this->evaluate($this->vars[$matches[1]]);
unset($this->stack[$matches[1]]);
return $this->inEval == false ? $return : '\'' . $return . '\'';
}
}
Edit 1: I tested the maximum recursion depth for this script using this:
$alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEF'; // GHIJKLMNOPQRSTUVWXYZ
$length = strlen($alphabet);
$vars['a'] = 'Hallo World!';
for ($i = 1; $i < $length; ++$i) {
$vars[$alphabet[$i]] = '%' . $alphabet[$i-1];
}
var_dump($vars);
$expression = new Evaluator($vars);
echo $expression->evaluate('%' . $alphabet[$length - 1]);
If another character is added to $alphabet maximum recursion depth of 100 is reached. (But probably you can modify this setting somewhere?)
I actually just did this while implementing a MVC framework.
What I did was create a "find-tags" function that uses a regular expression to find all things that should be replaced using preg_match_all and then iterated through the list and called the function recursively with the str_replaced code.
VERY Simplified Code
function findTags($body)
{
$tagPattern = '/{%(?P<tag>\w+) *(?P<inputs>.*?)%}/'
preg_match_all($tagPattern,$body,$results,PREG_SET_ORDER);
foreach($results as $command)
{
$toReturn[] = array(0=>$command[0],'tag'=>$command['tag'],'inputs'=>$command['inputs']);
}
if(!isset($toReturn))
$toReturn = array();
return $toReturn;
}
function renderToView($body)
{
$arr = findTags($body);
if(count($arr) == 0)
return $body;
else
{
foreach($arr as $tag)
{
$body = str_replace($tag[0],$LOOKUPARRY[$tag['tag']],$body);
}
}
return renderToView($body);
}