Extracting content between last 2 slashes with pregex - php

I have urls such as
/blah/WHATEVER/sites/two/one/blah/need/this.ini
in PHP, how do I extract /need/this.ini with regular expressions?

Without:
$url = '/blah/WHATEVER/sites/two/one/blah/need/this.ini';
$array = explode('/',$url);
$rev = array_reverse($array);
$last = $rev[0];
$second_last = $rev[1];
// or $rev[1].'/'.$rev[0]
A bit longer, I'm sure there are even better and clearer ways than this. Anyway, just to say you don't need regexes for this kind of stuff. Regexes are not a solution for everything :)
If you don't need the array intact, you can also array_pop() twice, and each time you get the last element. But you will shorten the array by one element each time.
Also:
$url = '/blah/WHATEVER/sites/two/one/blah/need/this.ini';
$array = explode('/',$url);
$last = end($array);
$second_last = prev($array);
See it in action

This should do:
(/[^/]+/[^/]+)$
(does not check for escaped slashes though.)

Notice # instead of /
if (preg_match('#/?[^/]+?/?[^/]+$#', $path, $m) !== false) {
// $m[0]` will contain the desired part
}
But there is better way to do this - don't use regexp at all:
function extract_part($path) {
$pos = strrpos( $path, '/');
if ($pos > 0) { // try to find the second one
$npath = substr($path, 0, $pos-1);
$npos = strrpos($npath, '/');
if ($npos !== false) {
return substr($path, $npos);
}
// This is not error, uncomment to change code behaviour.
/*else { // Returns '/this.ini' in your case
return substr($path, $pos);
}*/
}
// Returns as is
return $path;
}
(I have no php interpreter under my hands, so the code is not checked).
Yep, there was an error :) and now its fixed.

Related

String between string with array in PHP, array order ISSUE

I'm facing an issue with a function that gets a string between two other strings.
function string_between($str, $starting_word, $ending_word) {
$subtring_start = strpos($str, $starting_word);
$subtring_start += strlen($starting_word);
foreach ($ending_word as $a){
$size = strpos($str, $a, $subtring_start) - $subtring_start;
}
return substr($str, $subtring_start, $size);
}
The issue is that the function searches for the first ending_word in the array.
An example will be easier to understand:
$array_a = ['the', 'amen']; // Starting strings
$array_b = [',', '.']; // Ending strings
$str = "Hello, the world. Then, it is over.";
Expected result:
"the world."
Current result:
"the world. Then,"
The function will think that the ending_word is "," because it is the first element met in the array_b. However, the text encounters first the '.' after the "the" starting word.
How can I make sure the function goes through the text and stops at the first element in the $str present in the array_b, whatever the position in the array?
Any idea?
Basically, you need to break outside of your foreach loop when $size > 0
That way it stops looping through your array when it finds the 1st occurrence. Here is the more complete code with other fixes:
function stringBetween($string, $startingWords, $endingWords) {
foreach ($startingWords as $startingWord) {
$subtringStart = strpos($string, $startingWord);
if ($subtringStart > 0) {
foreach ($endingWords as $endingWord){
$size = strpos($string, $endingWord, $subtringStart) - $subtringStart + strlen($endingWord);
if ($size > 0) {
break;
}
}
if ($size > 0) {
return substr($string, $subtringStart, $size);
}
}
}
return null;
}
$startArr = array('the', 'amen'); // Starting strings
$endArr = array('.', ','); // Ending strings
$str = "Hello, the world. Then, it is over.";
echo stringBetween($str, $startArr, $endArr); // the world.
This type of problems are best solved by PCRE regexes, only couple of lines needed in function :
function string_between($str, $starts, $ends) {
preg_match("/(?:{$starts}).*?(?:{$ends})/mi", $str, $m);
return $m[0];
}
Then calling like this :
echo string_between("Hello, the world. Then, it is over.", 'the|amen', ',|\.');
Produces : the world.
The trick,- search to the nearest matching ending symbol is done with regex non-greedy seach, indicated by question symbol in pattern .*?. You can even extend this function to accept arrays as starting/ending symbols, just that case modify function (possibly with implode('|',$arr)) for concatenating symbols into regex grouping formula.
Edited version
This works now. Iterate over your teststrings from first array looking for position of occurance from teststring. If found one then search for the second teststring at startposition from end of first string.
To get the shortest hit I store the position from the second and take the minimum.
You can try it at http://sandbox.onlinephpfunctions.com/code/0f1e5c97da62b4daaf0e49f52271fe288d1cacbb
$array_a =array('the','amen');
$array_b =array(',','.', '#');
$str = "Hello, the world. Then, it is over.";
function earchString($str, $array_a, $array_b) {
forEach($array_a as $test) {
$pos = strpos($str, $test);
if ($pos===false) continue;
$found = [];
forEach($array_b as $test2) {
$posStart = $pos+strlen($test);
$pos2 = strpos($str, $test2, $posStart);
$found[] = ($pos2!==false) ? $pos2 : INF;
}
$min = min($found);
if ($min !== INF)
return substr($str,$pos,$min-$pos) .$str[$min];
}
return '';
}
echo earchString($str, $array_a, $array_b);

Retrieve word from string

I have this code:
$getClass = $params->get('pageclass_sfx');
var_dump($getClass); die();
The code above returns this:
string(24) "sl-articulo sl-categoria"
How can I retrieve the specific word I want without mattering its position?
Ive seen people use arrays for this but that would depend on the position (I think) that you enter these strings and these positions may vary.
For example:
$myvalue = $params->get('pageclass_sfx');
$arr = explode(' ',trim($myvalue));
echo $arr[0];
$arr[0] would return: sl-articulo
$arr[1] would return: sl-categoria
Thanks.
You can use substr for that in combination with strpos:
http://nl1.php.net/substr
http://nl1.php.net/strpos
$word = 'sl-categoria';
$page_class_sfx = $params->get('page_class_sfx');
if (false !== ($pos = strpos($page_class_sfx, $word))) {
// stupid because you already have the word... But this is what you request if I understand correctly
echo 'found: ' . substr($page_class_sfx, $pos, strlen($word));
}
Not sure if you want to get a word from the string if you already know the word... You want to know if it's there? false !== strpos($page_class_sfx, $word) would be enough.
If you know exactly what strings you're looking for, then stripos() should be sufficient (or strpos() if you need case-sensitivity). For example:
$myvalue = $params->get('pageclass_sfx');
$pos = stripos($myvalue, "sl-articulo");
if ($pos === FALSE) {
// string "sl-articulo" was not found
} else {
// string "sl-articulo" was found at character position $pos
}
If you need to check if some word are in string you may use preg_match function.
if (preg_match('/some-word/', 'many some-words')) {
echo 'some-word';
}
But this solution can be used for a small list of needed words.
For other cases i suggest you to use some of this.
$myvalue = $params->get('pageclass_sfx');
$arr = explode(' ',trim($myvalue));
$result = array();
foreach($arr as $key=> $value) {
// This will calculates all data in string.
if (!isset($result[$value])) {
$result[$value] = array(); // or 0 if you don`t need to use positions
}
$result[$value][] = $key; // For all positions
// $result[$value] ++; // For count of this word in string
}
// You can just test some words like follow:
if (isset($result['sl-categoria'])) {
var_dump($result['sl-categoria']);
}

PHP: normalize path of not existing directories to prevent directory traversals?

I would like to normalize a path from an external resource to prevent directory traversal attacks. I know about the realpath() function, but sadly this function returns only the path of existing directories. So if the directory doesn't exist (yet) the realpath() function cuts off the whole part of the path which doesn't exist.
So my Question is: Do you know a PHP function which only normalizes the path?
PS: I also don't want to create all possible directories in advance ;-)
There's no built-in PHP function for this. Use something like the following instead:
function removeDots($path) {
$root = ($path[0] === '/') ? '/' : '';
$segments = explode('/', trim($path, '/'));
$ret = array();
foreach($segments as $segment){
if (($segment == '.') || strlen($segment) === 0) {
continue;
}
if ($segment == '..') {
array_pop($ret);
} else {
array_push($ret, $segment);
}
}
return $root . implode('/', $ret);
}
Thanks to Benubird / Cragmonkey corrected me that under some situation my previous answer didn't work.
thus I make a new one, for the original purpose: Perform good, fewer lines, and with pure regular expression:
This time I tested with much more strict test case as below.
$path = '/var/.////./user/./././..//.//../////../././.././test/////';
function normalizePath($path) {
$patterns = array('~/{2,}~', '~/(\./)+~', '~([^/\.]+/(?R)*\.{2,}/)~', '~\.\./~');
$replacements = array('/', '/', '', '');
return preg_replace($patterns, $replacements, $path);
}
The correct answer would be /test/.
Not meant to do competition, but performance test is a must:
test case:
for loop 100k times, on an Windows 7, i5-3470 Quad Core, 3.20 GHz.
mine: 1.746 secs.
Tom Imrei: 4.548 secs.
Benubird: 3.593 secs.
Ursa: 4.334 secs.
It doesn't means my version is always better. In several situation they perform simular.
I think Tamas' solution will work, but it is also possible to do it with regex, which may be less efficient but looks neater. Val's solution is incorrect; but this one works.
function normalizePath($path) {
do {
$path = preg_replace(
array('#//|/\./#', '#/([^/.]+)/\.\./#'),
'/', $path, -1, $count
);
} while($count > 0);
return $path;
}
Yes, it does not handle all the possible different encodings of ./\ etc. that there can be, but that is not the purpose of it; one function should do one thing only, so if you want to also convert %2e%2e%2f into ../, run it through a separate function first.
Realpath also resolves symbolic links, which is obviously impossible if the path doesn't exist; but we can strip out the extra '/./', '/../' and '/' characters.
Strict, but safe implementation. If you use only ASCII for file names it would be suitable:
/**
* Normalise a file path string so that it can be checked safely.
*
* #param $path string
* The path to normalise.
* #return string
* Normalised path or FALSE, if $path cannot be normalized (invalid).
*/
function normalisePath($path) {
// Skip invalid input.
if (!isset($path)) {
return FALSE;
}
if ($path === '') {
return '';
}
// Attempt to avoid path encoding problems.
$path = preg_replace("/[^\x20-\x7E]/", '', $path);
$path = str_replace('\\', '/', $path);
// Remember path root.
$prefix = substr($path, 0, 1) === '/' ? '/' : '';
// Process path components
$stack = array();
$parts = explode('/', $path);
foreach ($parts as $part) {
if ($part === '' || $part === '.') {
// No-op: skip empty part.
} elseif ($part !== '..') {
array_push($stack, $part);
} elseif (!empty($stack)) {
array_pop($stack);
} else {
return FALSE; // Out of the root.
}
}
// Return the "clean" path
$path = $prefix . implode('/', $stack);
return $path;
}
My 2 cents. The regexp is used only for empty blocks of path:
<?php
echo path_normalize('/a/b/c/../../../d/e/file.txt');
echo path_normalize('a/b/../c');
echo path_normalize('./../../etc/passwd');
echo path_normalize('/var/user/.///////././.././.././././test/');
function path_normalize($path){
$path = str_replace('\\','/',$path);
$blocks = preg_split('#/#',$path,null,PREG_SPLIT_NO_EMPTY);
$res = array();
while(list($k,$block) = each($blocks)){
switch($block){
case '.':
if($k == 0)
$res = explode('/',path_normalize(getcwd()));
break;
case '..';
if(!$res) return false;
array_pop($res);
break;
default:
$res[] = $block;
break;
}
}
return implode('/',$res);
}
?>

How to mathematically evaluate a string like "2-1" to produce "1"?

I was just wondering if PHP has a function that can take a string like 2-1 and produce the arithmetic result of it?
Or will I have to do this manually with explode() to get the values left and right of the arithmetic operator?
I know this question is old, but I came across it last night while searching for something that wasn't quite related, and every single answer here is bad. Not just bad, very bad. The examples I give here will be from a class that I created back in 2005 and spent the past few hours updating for PHP5 because of this question. Other systems do exist, and were around before this question was posted, so it baffles me why every answer here tells you to use eval, when the caution from PHP is:
The eval() language construct is very dangerous because it allows execution of arbitrary PHP code. Its use thus is discouraged. If you have carefully verified that there is no other option than to use this construct, pay special attention not to pass any user provided data into it without properly validating it beforehand.
Before I jump in to the example, the places to get the class I will be using is on either PHPClasses or GitHub. Both the eos.class.php and stack.class.php are required, but can be combined in to the same file.
The reason for using a class like this is that it includes and infix to postfix(RPN) parser, and then an RPN Solver. With these, you never have to use the eval function and open your system up to vulnerabilities. Once you have the classes, the following code is all that is needed to solve a simple (to more complex) equation such as your 2-1 example.
require_once "eos.class.php";
$equation = "2-1";
$eq = new eqEOS();
$result = $eq->solveIF($equation);
That's it! You can use a parser like this for most equations, however complicated and nested without ever having to resort to the 'evil eval'.
Because I really don't want this only only to have my class in it, here are some other options. I am just familiar with my own since I've been using it for 8 years. ^^
Wolfram|Alpha API
Sage
A fairly bad parser
phpdicecalc
Not quite sure what happened to others that I had found previously - came across another one on GitHub before as well, unfortunately I didn't bookmark it, but it was related to large float operations that included a parser as well.
Anyways, I wanted to make sure an answer to solving equations in PHP on here wasn't pointing all future searchers to eval as this was at the top of a google search. ^^
$operation='2-1';
eval("\$value = \"$operation\";");
or
$value=eval("return ($operation);");
This is one of the cases where eval comes in handy:
$expression = '2 - 1';
eval( '$result = (' . $expression . ');' );
echo $result;
You can use BC Math arbitrary precision
echo bcsub(5, 4); // 1
echo bcsub(1.234, 5); // 3
echo bcsub(1.234, 5, 4); // -3.7660
http://www.php.net/manual/en/function.bcsub.php
In this forum someone made it without eval. Maybe you can try it? Credits to them, I just found it.
function calculate_string( $mathString ) {
$mathString = trim($mathString); // trim white spaces
$mathString = ereg_replace ('[^0-9\+-\*\/\(\) ]', '', $mathString); // remove any non-numbers chars; exception for math operators
$compute = create_function("", "return (" . $mathString . ");" );
return 0 + $compute();
}
$string = " (1 + 1) * (2 + 2)";
echo calculate_string($string); // outputs 8
Also see this answer here: Evaluating a string of simple mathematical expressions
Please note this solution does NOT conform to BODMAS, but you can use brackets in your evaluation string to overcome this.
function callback1($m) {
return string_to_math($m[1]);
}
function callback2($n,$m) {
$o=$m[0];
$m[0]=' ';
return $o=='+' ? $n+$m : ($o=='-' ? $n-$m : ($o=='*' ? $n*$m : $n/$m));
}
function string_to_math($s){
while ($s != ($t = preg_replace_callback('/\(([^()]*)\)/','callback1',$s))) $s=$t;
preg_match_all('![-+/*].*?[\d.]+!', "+$s", $m);
return array_reduce($m[0], 'callback2');
}
echo string_to_match('2-1'); //returns 1
As create_function got deprecated and I was utterly needed an alternative lightweight solution of evaluating string as math. After a couple of hours spending, I came up with following. By the way, I did not care about parentheses as I don't need in my case. I just needed something that conform operator precedence correctly.
Update: I have added parentheses support as well. Please check this project Evaluate Math String
function evalAsMath($str) {
$error = false;
$div_mul = false;
$add_sub = false;
$result = 0;
$str = preg_replace('/[^\d\.\+\-\*\/]/i','',$str);
$str = rtrim(trim($str, '/*+'),'-');
if ((strpos($str, '/') !== false || strpos($str, '*') !== false)) {
$div_mul = true;
$operators = array('*','/');
while(!$error && $operators) {
$operator = array_pop($operators);
while($operator && strpos($str, $operator) !== false) {
if ($error) {
break;
}
$regex = '/([\d\.]+)\\'.$operator.'(\-?[\d\.]+)/';
preg_match($regex, $str, $matches);
if (isset($matches[1]) && isset($matches[2])) {
if ($operator=='+') $result = (float)$matches[1] + (float)$matches[2];
if ($operator=='-') $result = (float)$matches[1] - (float)$matches[2];
if ($operator=='*') $result = (float)$matches[1] * (float)$matches[2];
if ($operator=='/') {
if ((float)$matches[2]) {
$result = (float)$matches[1] / (float)$matches[2];
} else {
$error = true;
}
}
$str = preg_replace($regex, $result, $str, 1);
$str = str_replace(array('++','--','-+','+-'), array('+','+','-','-'), $str);
} else {
$error = true;
}
}
}
}
if (!$error && (strpos($str, '+') !== false || strpos($str, '-') !== false)) {
$add_sub = true;
preg_match_all('/([\d\.]+|[\+\-])/', $str, $matches);
if (isset($matches[0])) {
$result = 0;
$operator = '+';
$tokens = $matches[0];
$count = count($tokens);
for ($i=0; $i < $count; $i++) {
if ($tokens[$i] == '+' || $tokens[$i] == '-') {
$operator = $tokens[$i];
} else {
$result = ($operator == '+') ? ($result + (float)$tokens[$i]) : ($result - (float)$tokens[$i]);
}
}
}
}
if (!$error && !$div_mul && !$add_sub) {
$result = (float)$str;
}
return $error ? 0 : $result;
}
Demo: http://sandbox.onlinephpfunctions.com/code/fdffa9652b748ac8c6887d91f9b10fe62366c650
Here is a somewhat verbose bit of code I rolled for another SO question. It does conform to BOMDAS without eval(), but is not equipped to do complex/higher-order/parenthetical expressions. This library-free approach pulls the expression apart and systematically reduces the array of components until all of the operators are removed. It certainly works for your sample expression: 2-1 ;)
preg_match() checks that each operator has a numeric substring on each side.
preg_split() divides the string into an array of alternating numbers and operators.
array_search() finds the index of the targeted operator, while it exists in the array.
array_splice() replaces the operator element and the elements on either side of it with a new element that contains the mathematical result of the three elements removed.
** updated to allow negative numbers **
Code: (Demo)
$expression = "-11+3*1*4/-6-12";
if (!preg_match('~^-?\d*\.?\d+([*/+-]-?\d*\.?\d+)*$~', $expression)) {
echo "invalid expression";
} else {
$components = preg_split('~(?<=\d)([*/+-])~', $expression, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
var_export($components); // ['-11','+','3','*','1','*','4','/','-6','-','12']
while (($index = array_search('*',$components)) !== false) {
array_splice($components, $index - 1, 3, $components[$index - 1] * $components[$index + 1]);
var_export($components);
// ['-11','+','3','*','4','/','-6','-','12']
// ['-11','+','12','/','-6','-','12']
}
while (($index = array_search('/', $components)) !== false) {
array_splice($components, $index - 1, 3, $components[$index - 1] / $components[$index + 1]);
var_export($components); // [-'11','+','-2','-','12']
}
while (($index = array_search('+', $components)) !== false) {
array_splice($components, $index - 1, 3, $components[$index - 1] + $components[$index + 1]);
var_export($components); // ['-13','-','12']
}
while (($index = array_search('-', $components)) !== false) {
array_splice($components, $index - 1, 3, $components[$index - 1] - $components[$index + 1]);
var_export($components); // [-25]
}
echo current($components); // -25
}
Here is a demo of the BOMDAS version that uses php's pow() when ^ is encountered between two numbers (positive or negative).
I don't think I'll ever bother writing a version that handles parenthetical expressions ... but we'll see how bored I get.
You can do it by eval function.
Here is how you can do this.
<?php
$exp = "2-1;";
$res = eval("return $exp");
echo $res; // it will return 1
?>
You can use $res anywhere in the code to get the result.
You can use it in form by changing $exp value.
Here is an example of creating a web calculator.
<?php
if (isset($_POST['submit'])) {
$exp = $_POST['calc'];
$res = eval("return $exp;"); //Here we have to add ; after $exp to make a complete code.
echo $res;
}
?>
// html code
<form method="post">
<input type="text" name="calc">
<input type="submit" name="submit">
</form>

String to array without explode

I am using an api to retrieve data from another server the data returned is something like this:
accountid=10110 type=prem servertime=1263752255 validuntil=1266163393
username= curfiles=11 curspace=188374868 bodkb=5000000 premkbleft=24875313
This is a whole string I need two values out of whole string, I am currently using preg_match to get it, but just to learn more and improve my coding is there any other way or function in which all values are automatically convert to array?
Thank You.
Sooo, my faster-than-preg_split, strpos-based function looks like this:
function unpack_server_data($serverData)
{
$output = array();
$keyStart = 0;
$keepParsing = true;
do
{
$keyEnd = strpos($serverData, '=', $keyStart);
$valueStart = $keyEnd + 1;
$valueEnd = strpos($serverData, ' ', $valueStart);
if($valueEnd === false)
{
$valueEnd = strlen($serverData);
$keepParsing = false;
}
$key = substr($serverData, $keyStart, $keyEnd - $keyStart);
$value = substr($serverData, $valueStart, $valueEnd - $valueStart);
$output[$key] = $value;
$keyStart = $valueEnd + 1;
}
while($keepParsing);
return $output;
}
It looks for an equals character, then looks for a space character, and uses these two to decide where a key name begins, and when a value name begins.
Using explode is the fastest for this, no matter what.
However, to answer you question, you can do this many ways. But just because you can, doesn't mean you should. But if you really wanna make it weird, try this.
UPdated using strpos
$arr = array();
$begin = 0;
$str = trim($str); # remove leading and trailing whitespace
while ($end = strpos($str, ' ', $begin)) {
$split = strpos($str, '=', $begin);
if ($split > $end) break;
$arr[substr($str, $begin, $split-$begin)] = substr($str, $split+1, $end-$split-1);
$begin = $end+1;
}
try out parse_str maybe you need to do str_replace(' ', '&', $string); before

Categories