PHP Regex to find first 3 match between slash - php

I have a string like this:
$url = '/controller/method/para1/para2/';
Expected output:
Array(
[0] => 'controller',
[1] => 'method',
[2] => array(
[0] => 'para1',
[1] => 'para2'
)
)
I am trying to build a regex to achieve this but not able to construct the pattern properly.
Please assist.
I tried to use explode function to split,
$split_url = explode('/',$url);
$controller = $split_url[1];
$method = $split_url[2];
unset($split_url[0]);
unset($split_url[1]);
unset($split_url[2]);
$para = $split_url;
But this is really not a great way of doing this and is prone to errors.

whithout regex:
$url = '/controller/method/para1/para2/para3/';
$arr = explode('/', trim($url, '/'));
$result = array_slice($arr, 0, 2);
$result[] = array_slice($arr, 2);
print_r($result);
Note: if you need to always have parameters at the same index (even if there is no method or parameters), you can change $result[] = array_slice($arr, 2); to $result[2] = array_slice($arr, 2);

Here's a slightly nasty method using explode:
$url = '/controller/method/para1/para2/para3/';
# get rid of leading and trailing slashes
$url = trim($url, '/');
$arr = explode('/', $url);
$results = array( $arr[0], $arr[1], array_slice($arr, 2) );
print_r($results);
Output:
Array
(
[0] => controller
[1] => method
[2] => Array
(
[0] => para1
[1] => para2
[2] => para3
)
)
It will work for any number of para elements.
And just to show that regexs are not scary, they're lovely fluffy friendly things, here's a regex version:
preg_match_all("/\/(\w+)/", $url, $matches);
$arr = $matches[1];
$results = array( $arr[0], $arr[1], array_slice($arr, 2) );
It's actually very easy to match this URL -- just search for / followed by alphanumeric characters (\w+).

How about something like:
$url = '/controller/method/para1/para2/para3/';
$regex = '~^/([^/]+)/([^/]+)/(?:(.*)/)?$~';
if(preg_match($regex, $url, $matches)) {
$controller = $matches[1];
$method = $matches[2];
$parameters = explode('/', $matches[3]);
}
This will capture 3 segments separated by a leading/trailing /. The 3rd segment of parameters can then be split with explode(). To get the array exactly like in your question:
$array = array($controller, $method, $parameters);
// Array
// (
// [0] => controller
// [1] => method
// [2] => Array
// (
// [0] => para1
// [1] => para2
// [2] => para3
// )
// )

An alterate way of thinking about this is to actually parse your route to determine the controller and then pass the remaining route components off to the controller to determine what to do.
$url = '/controller/method/para1/para2/para3/';
$route_parts = explode('/', $url, '/')); // we don't need leading and trailing forward slashes
$controller_str = array_shift($route_parts);
$method_str = array_shift($route_parts);
// instantiate controller object be some means (a factory pattern shown here for demo purposes)
$controller = controllerFactory::getInstance($controller_str);
// set method on controller
$controller->setMethod($method_str);
// pass parameters to controller
$controller->setParams($route_parts);
// do whatever with controller
$controller->execute();

Related

How to get an associative array from a string?

This is the initial string:-
NAME=Marco\nLOCATION=localhost\nSECRET=fjsdgfsjfdskffuv=\n
This is my solution although the "=" in the end of the string does not appear in the array
$env = file_get_contents(base_path() . '/.env');
// Split string on every " " and write into array
$env = preg_split('/\s+/', $env);
//create new array to push data in the foreach
$newArray = array();
foreach($env as $val){
// Split string on every "=" and write into array
$result = preg_split ('/=/', $val);
if($result[0] && $result[1])
{
$newArray[$result[0]] = $result[1];
}
}
print_r($newArray);
This is the result I get:
Array ( [Name] => Marco [LOCATION] => localhost [SECRET] => fjsdgfsjfdskffuv )
But I need :
Array ( [Name] => Marco [LOCATION] => localhost [SECRET] => fjsdgfsjfdskffuv= )
You can use the limit parameter of preg_split to make it only split the string once
http://php.net/manual/en/function.preg-split.php
you should change
$result = preg_split ('/=/', $val);
to
$result = preg_split ('/=/', $val, 2);
Hope this helps
$string = 'NAME=Marco\nLOCATION=localhost\nSECRET=fjsdgfsjfdskffuv=\n';
$strXlate = [ 'NAME=' => '"NAME":"' ,
'LOCATION=' => '","LOCATION":"',
'SECRET=' => '","SECRET":"' ,
'\n' => '' ];
$jsonified = '{'.strtr($string, $strXlate).'"}';
$array = json_decode($jsonified, true);
This is based on 1) translation using strtr(), preparing an array in json format and then using a json_decode which blows it up nicely into an array...
Same result, other approach...
You can also use parse_str to parse URL syntax-like strings to name-value pairs.
Based on your example:
$newArray = [];
$str = file_get_contents(base_path() . '/.env');
$env = explode("\n", $str);
array_walk(
$env,
function ($i) use (&$newArray) {
if (!$i) { return; }
$tmp = [];
parse_str($i, $tmp);
$newArray[] = $tmp;
}
);
var_dump($newArray);
Of course, you need to put some sanity check in the function since it can insert some strange stuff in the array like values with empty string keys, and whatnot.

How to get url directory names in php array?

I have url like https://in.pinterest.com/sridharposnic/restinpeace/.
I want url directories without domain in php array.
Example:-
$array[0] = 'sridharposnic';
$array[1] = 'restinpeace';
How we can extract these ?
You can use parse_url() and explode():
$url = 'https://in.pinterest.com/sridharposnic/restinpeace/';
$parsed = parse_url( $url );
$chunks = explode( '/', trim($parsed['path'],'/') );
print_r( $chunks );
Will print:
Array
(
[0] => sridharposnic
[1] => restinpeace
)

Reduce URL strings with no duplicates

I have an array that looks like the following...
$urls = array(
"http://www.google.com",
"http://www.google.com/maps",
"http://www.google.com/mail",
"https://drive.google.com",
"https://www.youtube.com",
"https://www.youtube.com/feed/subscriptions",
"https://www.facebook.com/me",
"https://www.facebook.com/me/friends"
);
I find this hard to explain but I want to break this array down to only show the reduced URLs with no duplicates, so it looks like this...
$urls = array(
"http://www.google.com",
"https://drive.google.com",
"https://www.youtube.com",
"https://www.facebook.com/me"
);
Notice the last URL in the second array still has it's path. This is because I want still want to show the lowest level paths
Based on #Tim's answer
foreach ($urls as &$url) {
$url_parts = parse_url($url);
$url = $url_parts["scheme"]."://".$url_parts["host"];
}
$urls = array_unique($urls);
Just sort the array in reverse order, and create an array indexed by host:
$urls = array(
"http://www.google.com",
"http://www.google.com/maps",
"http://www.google.com/mail",
"https://drive.google.com",
"https://www.youtube.com",
"https://www.youtube.com/feed/subscriptions",
"https://www.facebook.com/me",
"https://www.facebook.com/me/friends"
);
rsort($urls);
$return = [];
foreach($urls as $url) {
$host = parse_url($url, PHP_URL_HOST);
$return[$host] = $url;
}
$return = array_values($return); // To remove array keys, if desired.
The reverse-ordered urls array would be:
Array
(
[0] => https://www.youtube.com/feed/subscriptions
[1] => https://www.youtube.com
[2] => https://www.facebook.com/me/friends
[3] => https://www.facebook.com/me
[4] => https://drive.google.com
[5] => http://www.google.com/maps
[6] => http://www.google.com/mail
[7] => http://www.google.com
)
Since the last entry (per host name) in the sorted array is the one that you want, and it deliberately clobbers any existing array value, this would output:
Array
(
[www.youtube.com] => https://www.youtube.com
[www.facebook.com] => https://www.facebook.com/me
[drive.google.com] => https://drive.google.com
[www.google.com] => http://www.google.com
)
Try this:
$result = array();
array_push($result, $urls[0])
for($i=1; $i<count($urls); $i++)
{
$repeat = false;
foreach($result as $res)
{
if(strpos($urls[i], $res))
{
$repeat = true;
break;
}
}
if(!repeat)
array_push($result, $urls[i])
}
return $result;

Get URLs lowest path by domain

I have an array that looks like the following...
$urls = array(
"http://www.google.com",
"http://www.google.com/maps",
"http://www.google.com/mail",
"https://drive.google.com/help",
"https://www.youtube.com",
"https://www.youtube.com/feed/subscriptions",
"https://www.facebook.com/me",
"https://www.facebook.com/me/friends"
);
I find this hard to explain but I want to break this array down to only show the lowest path for each domain with no duplicates, so it looks like this...
$urls = array(
"http://www.google.com",
"https://drive.google.com/help",
"https://www.youtube.com",
"https://www.facebook.com/me"
);
This can be achieved by walking through the array and inspecting the host key by using parse_url(). The following logic will give your desired result.
$output = array();
//Sort the array by character length
usort($urls, function($a, $b) {
return strlen($a)-strlen($b);
});
array_walk($urls, function($url) use (&$output) {
//Parse the URL to get its components
$parsed_url = parse_url($url);
//See if we've already added the host to our final array
if( array_key_exists($parsed_url['host'], $output) === FALSE ) {
//We haven't, so we can now add the url to our final array
$output[$parsed_url['host']] = $url;
}
});
https://eval.in/415655
try this,
$urls = array(
"http://www.google.com",
"http://www.google.com/maps",
"http://www.google.com/mail",
"https://drive.google.com/help",
"https://www.youtube.com",
"https://www.youtube.com/feed/subscriptions",
"https://www.facebook.com/me",
"https://www.facebook.com/me/friends"
);
$temp = array();
$res = array();
usort($urls, function($a, $b) {
return strlen($a)-strlen($b);
});//sort the array based string length
foreach($urls as $url){
$str = preg_replace('#^https?://#', '', $url);
$strarray = explode("/", $str);
if(!in_array($strarray[0], $temp)){
$temp[] = $strarray[0];
$res[] = $url;
}
}
echo"<pre>";
print_r($res);
echo"</pre>";
output:
Array
(
[0] => http://www.google.com
[1] => https://www.youtube.com
[2] => https://www.facebook.com/me
[3] => https://drive.google.com/help
)

Regex Multiple Capture of Group

I'm using regex to capture the dimensions of ads
Source content is an HTML File, and I'm trying to capture for content that looks like:
size[200x400,300x1200] (could be 1-4 different sizes)
I'm trying to an array with the different sizes in it
My capture code looks like this:
$size_declaration = array();
$sizes = array();
$declaration_pattern = "/size\[(\d{2,4}x\d{2,4}|\d{2,4}x\d{2,4},){1,4}\]/";
$sizes_pattern = "/\d{2,4}x\d{2,4}/";
$result = preg_match($declaration_pattern, $html, $size_declaration);
if( $result ) {
$result = preg_match_all($sizes_pattern, $size_declaration[0], $sizes);
var_dump($sizes);
}
The code above produces usable results:
$sizes = array(
[0] => array (
[0] => '200x400',
[1] => '300x1200'
)
)
but it takes quite a bit of code. I was thinking it was possible to collect the results with a single regex, but I couldn't find a result that works. Is there a way to clean this up a bit?
It's not very practical to turn it into a single expression; it would be better to keep them separate; the first expression finds the boundaries and does rudimentary content checks on the inner contents, the second expression breaks it down into individual pieces:
if (preg_match_all('/size\[([\dx,]+)\]/', $html, $matches)) {
foreach ($matches[0] as $size_declaration) {
if (preg_match_all('/\d+x\d+/', $size_declaration, $sizes)) {
print_r($sizes[0]);
}
}
}
This one is a little simpler:
$html = "size[200x400,300x600,300x100]";
if (($result = preg_match_all("/(\d{2,4}x\d{2,4}){1,4}/", $html, $matches)) > 0)
var_dump($matches);
//
// $matches =>
// array(
// (int) 0 => array(
// (int) 0 => '200x400',
// (int) 1 => '300x600',
// (int) 2 => '300x100'
// ),
// (int) 1 => array(
// (int) 0 => '200x400',
// (int) 1 => '300x600',
// (int) 2 => '300x100'
// )
// )
//
The only way is to repeat the 4 eventual sizes in the pattern:
$subject = <<<LOD
size[523x800]
size[200x400,300x1200]
size[201x300,352x1200,123x456]
size[142x396,1444x32,143x89,231x456]
LOD;
$pattern = '`size\[(\d{2,4}x\d{2,4})(?:,(\d{2,4}x\d{2,4}))?(?:,(\d{2,4}x\d{2,4}))?(?:,(\d{2,4}x\d{2,4}))?]`';
preg_match_all($pattern, $subject, $matches, PREG_SET_ORDER);
foreach ($matches as &$match) { array_shift($match); }
print_r($matches);
The pattern can also be shorten using references to capture groups:
$pattern = '`size\[(\d{2,4}x\d{2,4})(?:,((?1)))?(?:,((?1)))?(?:,((?1)))?]`';
or with the Oniguruma syntax:
$pattern = '`size\[(\d{2,4}x\d{2,4})(?:,(\g<1>))?(?:,(\g<1>))?(?:,(\g<1>))?]`';

Categories