Check if string equals, starts withs or ends with match - php

How would I improve this functions for that is:
Searches an array for fields names which either:
a) Exactly matches
b) beings with "_" check if string begins with
c) ends with "_" check if string ends with
E.g I have a list of column names:
array(
'customer_name',
'customer_lastname',
'customer_streetname',
'customer_dob',
'system_modified'
)
And another array with formatting conditions:
array(
'_dob' => 'date_dob',
'_name' => 'varchar',
'customer_name' => 'html_text',
'system_' => 'required'
)
Results apply the conditions against the column names:
1. customer_name = html_text (exact matches have higher preference)
2. customer_lastname = varchar
3. customer_streetname =
4. customer_dob = dob
5. system_modified = required
Current have this:
protected function matchPatterns($string) {
$return = array();
$parrerns = $this->_normaliseArrayItem($this->getPatterns());
foreach ($parrerns as $match) {
// If exact match
if($string == $match) {
$return[] = $match;
break;
// Else if begins with _ and ends with string.
} elseif($string[0] == "_" && substr_compare($string, $match, -strlen($match), strlen($match)) === 0) {
$return[] = $match;
}
} // end loop
return $return;
}
/**
* Return an array of validation patterns.
*
* #return string[]
*/
public function getPatterns() {
return $this->_patterns;
}
/**
* Returns an item as array rather than single item.
*
* #param string[] $data
* #return string[]
*/
protected function _normaliseArrayItem($data) {
if(!isset($data[0])|| !is_array($data)) {
$tmp[] = $data;
$data = $tmp;
}
return $data;
}

foreach ($parrerns as $match => $format) {
// If exact match
if ($string == $match) {
$return[] = $format;
break;
// Else if begins with _ and string ends with it.
} elseif ($match[0] == "_" && substr_compare($string, $match, -strlen($match), strlen($match)) === 0) {
$return[] = $format;
// Else if ends with _ and string begins with it
} elseif (substr($match, -1) == "_" && substr_compare($string, $match, 0, strlen($match)) == 0) {
$return[] = $format;
}
}

Adapted from Marc's solution.
if(substr($str, 0, 1) === '_' || substr($str, -1) === '_') {
// it starts or ends with an underscore
} else if($str == $match) {
// it's the same
}
I don't entirely understand your question though.

Related

how to extract values from url and match them using preg_match

$pathInfo = 'store/view/14342/galaxy-s10-lite-sm-g770-8gb';
I have a route array as -
$route = [
'store/{id}/{pid}' => ['home/store', ['id', 'exuo', 'pid']],
'store/{id}' => ['home/store', ['id']],
'store/view/{id}/{name}' => ['home/store', ['id','name']], // pathInfo should match this route
];
How do I match the the $pathInfo with its corresponding route.
this is how i tried to do it -
public function process_route() {
if (is_array($this->routes()) && count($this->routes()) > 0) {
//print_r([$this->rawPathInfo, $this->request, $this->route]) . "<br>";
foreach ($this->routes() as $pattern => $rules) {
$match = str_replace('/', '\/', $pattern);
if (preg_match("/$match/", $this->pathInfo)) {
if (count(explode('/', $this->pathInfo)) == count(explode('/', $pattern))) {
$this->set_params($rules);
return $rules[0];
}
}
}
}
return FALSE;
}
protected function set_params($rules) {
if (count($rules) >= 2) {
if (is_array($rules[1]) && count($rules) >= 2) {
$pathInfoArray = explode("/", $this->pathInfo);
foreach ($rules[1] as $key) {
$index1 = array_search($key, $pathInfoArray);
$value = (isset($pathInfoArray[$index1 + 1])) ? $pathInfoArray[$index1 + 1] : self::$NOT_FOUND;
if ($value !== self::$NOT_FOUND)
$_GET[$key] = $value;
}
}
}
}
the only diff is here I defined the routes as
$routes =[
'store/id/.*/exuo/.*/pid/.*' => ['home/store', ['id', 'exuo', 'pid']],
];
and was matching the values with the (.*) fields.
You could transform your route paths into appropriate regular expressions, check $pathInfo against each of them, then return the first one which matches (if any):
/**
* #param string[] $routes
*/
function findRoute(array $routes, string $pathInfo): ?string
{
foreach (array_keys($routes) as $routePath) {
$pattern = '~^' . preg_replace('/{.*?}/', '[^/]+', $routePath) . '$~';
if (preg_match($pattern, $pathInfo)) {
return $routePath;
}
}
return null;
}
Usage:
findRoute($routes, $pathInfo);
Demo: https://3v4l.org/DoimK

How to get the reverse routing?

I have a rules in routing config
/**
* Format
* [controller, action($id,...), module]
*
* s: - string
* i: - integer
* {placeholder} - for replace in URL
*/
$rules = [
'/' => ['site', 'index'],
'/[s:action]' => ['site', '{action}'],
'/[s:action]/[s:controller]' => ['{controller}', '{action}'],
'/[s:module]/[s:controller]/[i:id]' => ['{controller}', 'view', '{module}'],
'/[s:module]/[s:controller]/[s:action]/[i:id]' => ['{controller}', '{action}', '{module}'],
'/[s:controller]/[i:id]/[i:id2]/[i:id3]' => ['{controller}', 'parse'],
];
How to get the reverse routing?
//Full URL => Rewrite URL
$results = [
'/' => '/',
'site/index' => '/',
'site/login' => '/login',
'info/user' => '/user/info',
'user/profile/view/123' => '/user/profile/123',
'user/profile/edit/123' => '/user/profile/edit/123',
];
How to do it correctly for best performance?
Update This is routing code
For parse pattern in previous step.
As I understand to be treated as templates and routing parameters, to form the final path.
function checkRule($path, $pattern) {
$params = [];
if ($pattern === '*') {
//Everyone
$match = true;
} elseif (isset($pattern[0]) && $pattern[0] === '#') {
//Custom regexp
$pattern = '`' . substr($pattern, 1) . '`u';
$match = preg_match($pattern, $path, $params);
} else {
//Parse pattern
$n = isset($pattern[0]) ? $pattern[0] : null;
$route = null;
$regex = false;
$j = 0;
$i = 0;
// Find the longest non-regex substring and match it against the URI
while (true) {
if (!isset($pattern[$i])) {
break;
}
if (false === $regex) {
$c = $n;
$regex = $c === '[' || $c === '(' || $c === '.';
if (false === $regex && false !== isset($pattern[$i + 1])) {
$n = $pattern[$i + 1];
$regex = $n === '?' || $n === '+' || $n === '*' || $n === '{';
}
if (false === $regex && $c !== '/' && (!isset($path[$j]) || $c !== $path[$j])) {
return null;
}
$j++;
}
$route .= $pattern[$i++];
}
$regex = self::compileRoute($route);
$match = preg_match($regex, $path, $params);
}
if ($match) {
return $params;
}
return null;
}
function compileRoute($route) {
if (preg_match_all('`(/|\.|)\[([^:\]]*+)(?::([^:\]]*+))?\](\?|)`', $route, $matches, PREG_SET_ORDER)) {
$matchTypes = self::$matchTypes;
foreach ($matches as $match) {
list($block, $pre, $type, $param, $optional) = $match;
if (isset($matchTypes[$type])) {
$type = $matchTypes[$type];
}
if ($pre === '.') {
$pre = '\.';
}
if ($param !== '') {
$param = "?P<{$param}>";
}
if ($optional !== '') {
$optional = '?';
}
$pattern = "(?:{$pre}({$param}{$type})){$optional}";
$route = str_replace($block, $pattern, $route);
}
}
return "`^{$route}$`u";
}

Is string a math expression?

How can I found if string is a math expression or not?
It is enough to understand basic math expressions +, -, x, /
For Example:
"1+1" => TRUE
"2 / 2" => TRUE
"hello" => FALSE
"1 * 2 - X" => FALSE
"me + u" => FALSE
class MathExpression {
private static $parentheses_open = array('(', '{', '[');
private static $parentheses_close = array(')', '}', ']');
protected static function getParenthesesType( $c ) {
if(in_array($c,MathExpression::$parentheses_open)) {
return array_search($c, MathExpression::$parentheses_open);
} elseif(in_array($c,MathExpression::$parentheses_close)) {
return array_search($c, MathExpression::$parentheses_close);
} else {
return false;
}
}
public static function validate( $expression ) {
$size = strlen( $expression );
$tmp = array();
for ($i=0; $i<$size; $i++) {
if(in_array($expression[$i],MathExpression::$parentheses_open)) {
$tmp[] = $expression[$i];
} elseif(in_array($expression[$i],MathExpression::$parentheses_close)) {
if (count($tmp) == 0 ) {
return false;
}
if(MathExpression::getParenthesesType(array_pop($tmp))
!= MathExpression::getParenthesesType($expression[$i])) {
return false;
}
}
}
if (count($tmp) == 0 ) {
return true;
} else {
return false;
}
}
}
//Mathematical expressions to validate
$tests = array(
'(A1+A2*A3)+A5+(B3^B5)*(C1*((A3/C2)+(B2+C1)))',
'(A1+A2*A3)+A5)*C1+(B3^B5*(C1*((A3/C2)+(B2+C1)))',
'(A1+A2*A3)+A5++(B2+C1)))',
'(A1+A2*A3)+A5+(B3^B5)*(C1*(A3/C2)+(B2+C1))'
);
// running the tests...
foreach($tests as $test) {
$isValid = MathExpression::validate( $test );
echo 'test of: '. $test .'<br>';
var_dump($isValid);
}
you can check and read in detail about the solution here Is there possible to check mathematical expression string?
See also eval. For example, you can do this:
$result = INF;
try {
eval("$result=" + myMathExpression); // Evaluate here
} catch (Exception $e) {
}
if($result != INF) echo("Expression is a valid mathematical expression.");
read more about it there
An extremely simple solution:
Regex number,whitespace,[+,/,*,-,=],whitespace,Substring(recursion here)
will work for any sequence of
1 + 1 + 2 + ... + 1 = 2 + 3 + 4 = 1 * 4 ...
etc.
Obviously would not check if an expression is legit.
As per request, pseudo code:
if Regex(^(([0-9]+)(\s*)([+,/,*,-,=])(\s*)([0-9]+)(\s*)([+,/,*,-,=])(\s*)))
if (recursion())
return True;
else
return False;
else //checking for end point
if Regex(^(([0-9]+)(\s*)([+,/,*,-,=])(\s*)([0-9]+)))
return True;
else
return False;
Maybe a regex with a pattern like this :
^([-+/*]\d+(\.\d+)?)*

Check if an array of words exist in a string

We have a function in PHP, in_array, which checks if the given value is present in an array. I guess I wanna do a reverse of it. I have a set of strings:
$words = array("hello", "world");
And, I wanna check, if a given string has these words, by giving parameters whether all or any.
$string = "Hello, World!";
$second = "Hello and Welcome!";
in_string($words, $string, "all");
in_string($words, $string, "any");
What I have right now is, using stripos(). I do not wanna use regex.
Current Code:
<?php
/**
* Checks if the given words is found in a string or not.
*
* #param Array $words The array of words to be given.
* #param String $string The string to be checked on.
* #param String $option all - should have all the words in the array. any - should have any of the words in the array
* #return boolean True, if found, False if not found, depending on the $option
*/
function in_string ($words, $string, $option)
{
if ($option == "all")
foreach ($words as $value)
$isFound = $isFound && (stripos($string, $value) || false);
else
foreach ($words as $value)
$isFound = $isFound || (stripos($string, $value) || false);
return $isFound;
}
?>
My question is, can this be improved? Any thoughts?
Update #1: Current Code Updated:
/**
* Checks if the given words is found in a string or not.
*
* #param Array $words The array of words to be given.
* #param String $string The string to be checked on.
* #param String $option all - should have all the words in the array. any - should have any of the words in the array
* #return boolean True, if found, False if not found, depending on the $option
*/
function in_string ($words, $string, $option)
{
if ($option == "all")
{
$isFound = true;
foreach ($words as $value)
$isFound = $isFound && (stripos($string, $value) !== false);
return $isFound;
}
else
{
$isFound = true;
foreach ($words as $value)
if (stripos($string, $value) !== false) return true;
return $isFound;
}
}
Now the function is working as expected. But I need a better performance in the foreach() part and all. Anything possible for improvements?
Update #2: Improvements Added
<?php
/**
* Checks if the given words is found in a string or not.
*
* #param Array $words The array of words to be given.
* #param String $string The string to be checked on.
* #param String $option all - should have all the words in the array. any - should have any of the words in the array
* #return boolean True, if found, False if not found, depending on the $option
*/
function in_string ($words, $string, $option)
{
if ($option == "all")
{
foreach ($words as $value)
if (stripos($string, $value) === false)
return false;
return true;
}
else
{
foreach ($words as $value)
if (stripos($string, $value) !== false)
return true;
return false;
}
}
?>
<?php
/**
* Checks if the given words is found in a string or not.
*
* #param Array $words The array of words to be given.
* #param String $string The string to be checked on.
* #param String $option all - should have all the words in the array. any - should have any of the words in the array
* #return boolean True, if found, False if not found, depending on the $option
*/
function in_string ($words, $string, $option)
{
if ($option == "all") {
$isFound = true;
foreach ($words as $value) {
$isFound = $isFound && (stripos($string, $value) !== false); // returns boolean false if nothing is found, not 0
if (!$isFound) break; // if a word wasn't found, there is no need to continue
}
} else {
$isFound = false;
foreach ($words as $value) {
$isFound = $isFound || (stripos($string, $value) !== false);
if ($isFound) break; // if a word was found, there is no need to continue
}
}
return $isFound;
}
?>
Just the same code as the person below me, with the break; added. I don't have enough rep to comment apparently
Divide the logic and you'll get simpler code in each function, better performance and each function will solve a concrete task.
<?php
function in_string_all ($string, $words) {
$isFound = true;
foreach ($words as $value) {
$isFound = $isFound && stripos($string, $value) !== false;
if (!$isFound)
break;
}
return $isFound;
}
function in_string_any ($string, $words) {
foreach ($words as $value)
if (stripos($string, $value) !== false)
return true;
return false;
}
?>
Using strpos does not work because it will return true for partial string matches. examples searching "in" in "insersection" results in a false positive.
Here is an alternative extracting words from the target string and making an intersection between arrays.
/**
* Checks if a array of given words is found in a string.
*
* #param array $words The array of words to be given.
* #param string $string The string to be checked on.
* #param boolean $exactMatch true - should have all the words in the array. false - should have any of the words in the array
* #param boolean $insensitive true to compare insensitive. false to compare sensitive
* #return boolean True, if found, False if not found, depending on the $option
*/
private function arrayInString (array $words, $string, $exactMatch = true, $insensitive = true)
{
$stringArr = explode(" ", preg_replace('/[^a-z0-9]+/i', ' ', $string));
if ($insensitive) {
$words = array_map('strtolower', $words);
$stringArr = array_map('strtolower', $stringArr);
}
$intersectionCount = count(array_intersect($words, $stringArr));
if ($exactMatch && $intersectionCount == count($words)) {
return true;
}
if (!$exactMatch && $intersectionCount > 0) {
return true;
}
return false;
}
Use strpos() instead of substr() or strstr() because you only care about the word being present, you don't need to parse anything out. According to the docs, it is faster.
From the Manual:
If you only want to determine if a particular needle occurs within haystack, use the faster and less memory intensive function strpos() instead.
You need to init $isFound, use stripos() for case-insensitive search, and check that stripos() returns false, not 0. (0 means that $string starts with $value)
function in_string ($words, $string, $option)
{
if ($option == "all") {
$isFound = true;
foreach ($words as $value) {
$isFound = $isFound && (stripos($string, $value) !== false);
}
} else {
$isFound = false;
foreach ($words as $value) {
$isFound = $isFound || (stripos($string, $value) !== false);
}
}
return $isFound;
}

Parsing Javascript (not JSON) in PHP

I have a php string containing the serialization of a javascript object :
$string = '{fu:"bar",baz:["bat"]}';
The actual string is far more complicated, of course, but still well-formed javascript. This is not standard JSON, so json_decode fails. Do you know any php library that would parse this string and return a php associative array ?
This sounded like a fun challenge, so I coded up a tiny parser :D
class JsParserException extends Exception {}
function parse_jsobj($str, &$data) {
$str = trim($str);
if(strlen($str) < 1) return;
if($str{0} != '{') {
throw new JsParserException('The given string is not a JS object');
}
$str = substr($str, 1);
/* While we have data, and it's not the end of this dict (the comma is needed for nested dicts) */
while(strlen($str) && $str{0} != '}' && $str{0} != ',') {
/* find the key */
if($str{0} == "'" || $str{0} == '"') {
/* quoted key */
list($str, $key) = parse_jsdata($str, ':');
} else {
$match = null;
/* unquoted key */
if(!preg_match('/^\s*[a-zA-z_][a-zA-Z_\d]*\s*:/', $str, $match)) {
throw new JsParserException('Invalid key ("'.$str.'")');
}
$key = $match[0];
$str = substr($str, strlen($key));
$key = trim(substr($key, 0, -1)); /* discard the ':' */
}
list($str, $data[$key]) = parse_jsdata($str, '}');
}
"Finshed dict. Str: '$str'\n";
return substr($str, 1);
}
function comma_or_term_pos($str, $term) {
$cpos = strpos($str, ',');
$tpos = strpos($str, $term);
if($cpos === false && $tpos === false) {
throw new JsParserException('unterminated dict or array');
} else if($cpos === false) {
return $tpos;
} else if($tpos === false) {
return $cpos;
}
return min($tpos, $cpos);
}
function parse_jsdata($str, $term="}") {
$str = trim($str);
if(is_numeric($str{0}."0")) {
/* a number (int or float) */
$newpos = comma_or_term_pos($str, $term);
$num = trim(substr($str, 0, $newpos));
$str = substr($str, $newpos+1); /* discard num and comma */
if(!is_numeric($num)) {
throw new JsParserException('OOPSIE while parsing number: "'.$num.'"');
}
return array(trim($str), $num+0);
} else if($str{0} == '"' || $str{0} == "'") {
/* string */
$q = $str{0};
$offset = 1;
do {
$pos = strpos($str, $q, $offset);
$offset = $pos;
} while($str{$pos-1} == '\\'); /* find un-escaped quote */
$data = substr($str, 1, $pos-1);
$str = substr($str, $pos);
$pos = comma_or_term_pos($str, $term);
$str = substr($str, $pos+1);
return array(trim($str), $data);
} else if($str{0} == '{') {
/* dict */
$data = array();
$str = parse_jsobj($str, $data);
return array($str, $data);
} else if($str{0} == '[') {
/* array */
$arr = array();
$str = substr($str, 1);
while(strlen($str) && $str{0} != $term && $str{0} != ',') {
$val = null;
list($str, $val) = parse_jsdata($str, ']');
$arr[] = $val;
$str = trim($str);
}
$str = trim(substr($str, 1));
return array($str, $arr);
} else if(stripos($str, 'true') === 0) {
/* true */
$pos = comma_or_term_pos($str, $term);
$str = substr($str, $pos+1); /* discard terminator */
return array(trim($str), true);
} else if(stripos($str, 'false') === 0) {
/* false */
$pos = comma_or_term_pos($str, $term);
$str = substr($str, $pos+1); /* discard terminator */
return array(trim($str), false);
} else if(stripos($str, 'null') === 0) {
/* null */
$pos = comma_or_term_pos($str, $term);
$str = substr($str, $pos+1); /* discard terminator */
return array(trim($str), null);
} else if(strpos($str, 'undefined') === 0) {
/* null */
$pos = comma_or_term_pos($str, $term);
$str = substr($str, $pos+1); /* discard terminator */
return array(trim($str), null);
} else {
throw new JsParserException('Cannot figure out how to parse "'.$str.'" (term is '.$term.')');
}
}
Usage:
$data = '{fu:"bar",baz:["bat"]}';
$parsed = array();
parse_jsobj($data, $parsed);
var_export($parsed);
Gives:
array (
'fu' => 'bar',
'baz' =>
array (
0 => 'bat',
),
)
Tested with these strings:
'{fu:"bar",baz:["bat"]}',
'{rec:{rec:{rec:false}}}',
'{foo:[1,2,[3,4]]}',
'{fu:{fu:"bar"},bar:{fu:"bar"}}',
'{"quoted key":[1,2,3]}',
'{und:undefined,"baz":[1,2,"3"]}',
'{arr:["a","b"],"baz":"foo","gar":{"faz":false,t:"2"},f:false}',
Pear Services_JSON will parse that string (tested version 1.31). But given that that is a JSON parser and that this isn't valid JSON you have no guarantee that future versions will still work.
I found out that the Yii-framework's CJSON::decode() function handles Javascript objects as well.
If you're not using Yii, you should be able to just use the source code
thank luttkens
the CJON::decode() class of the Yii-framework works perfectly !
require_once ($_SERVER['DOCUMENT_ROOT']."/phplib/CJSON.php");
$json = new CJSON();
$data = $json->decode('{ url : "/jslib/maps/marker/marker_red.png", height : 34, width : 20, anchorIcon : [5,25.5], anchorText : [0,2], }', true);
print_r( $data );
result :
Array
(
[url] => /jslib/maps/marker/marker_red.png
[height] => 34
[width] => 20
[anchorIcon] => Array
(
[0] => 5
[1] => 25.5
)
[anchorText] => Array
(
[0] => 0
[1] => 2
)
)
What about that library?
http://timwhitlock.info/tag/jparser/
I haven't tried it yet.

Categories