I have a php search code that returns the results in the page. What I wanna do is limit the number of results per page to 10, and echo a link for more results.
My code is:
$dir = 'www/posts';
$exclude = array('.','..','.htaccess');
$q = (isset($_GET['q']))? strtolower($_GET['q']) : '';
if (!empty($q)) {
$res = opendir($dir);
while(false!== ($file = readdir($res))) {
if(strpos(strtolower($file),$q)!== false &&!in_array($file,$exclude)) {
$last_dot_index = strrpos($file, ".");
$withoutExt = substr($file, 0, $last_dot_index);
$fpath = 'posts/'.$file;
if (file_exists($fpath)) {
echo "<a href='/search.php?post=$withoutExt'>$withoutExt</a>" . " on " . date ("d F Y ", filemtime($fpath)) ;
echo "<br>";
}
}
}
closedir($res);
}
else {
echo "";
}
I tried the $q->limit(10); but it doesnt work. Please help me write a working code.
<?php
$dir = 'www/posts';
$exclude = array(
'.',
'..',
'.htaccess'
);
$q = (isset($_GET['q'])) ? strtolower($_GET['q']) : '';
$results=[];
if (! empty($q)) {
$res = opendir($dir);
while (false !== ($file = readdir($res))) {
if (strpos(strtolower($file), $q) !== false && ! in_array($file, $exclude)) {
$last_dot_index = strrpos($file, ".");
$withoutExt = substr($file, 0, $last_dot_index);
$fpath = 'posts/' . $file;
if (file_exists($fpath)) {
$results[]="<a href='/search.php?post=$withoutExt'>$withoutExt</a>" . " on " . date("d F Y ", filemtime($fpath));
}
}
}
closedir($res);
}
foreach ($results as $result) {
echo $result;
echo "<br>";
}
if (count($results) > 10 ) {
echo 'results is more than 10, but only 10 results is shown';
}
Here is a completely untested suggestion... I am happy to have a bit of back and forth to iron it out.
if(!empty($_GET['q'])){
// perform some logical validation/sanitization on `$_GET['q']` for stability/security
$path = '../posts'; // this relative path may need some adjusting
$files=glob("$path/*.txt"); // collect all .txt files from the directory
var_export($files);
$filtered=preg_grep('~.*'.preg_quote($q,'/').'.*\.txt$~i',$files); // case-insensitive filename search
var_export($filtered);
if(!empty($filtered)){
$batches=array_chunk($filtered,10); // store in groups of 10
var_export($batches); // check the data
if(!isset($_GET['page'],$batches[--$page])){ // a page has been submitted and that page exists in the batches array (subtract one to sync the page with the index)
$page=0;
}
$leftovers=array_keys(array_diff_key($batches,[$page=>'']));
foreach($batches[$page] as $filename){
$withoutExt=substr($filename,0,strrpos($filename,"."));
$fpath="posts/$filename";
echo "<div><a href='/search.php?post=$withoutExt'>$withoutExt</a> on ",date("d F Y",filemtime($fpath)),"</div>";
}
// here you can list the other pages available from this search
if($leftovers){
echo "Additional pages:";
foreach($leftovers as $offset){
echo " <a href='/search.php?q={$_GET['q']}&page=",++$offset,"'>$offset</a>";
}
}
}
}else {
echo "No Search Terms found";
}
I have on my linux machine such folder tree structure:
/dir/yyyy/mm/dd/HH
e.g.:
/dir/2014/03/01/08
/dir/2014/03/20/09
/dir/2014/03/01/10
/dir/2014/08/01/10
/dir/2014/12/15/10
/dir/2015/01/01/14
I'd like to get in php what path is the oldest, like this:
The oldest path is: 2014-03-01 08
The newest path is: 2015-01-01 14
How it can be done?
It could be written better but it works
$paths = array(
'/dir/2014/03/01/08',
'/dir/2014/03/20/09',
'/dir/2014/03/01/10',
'/dir/2014/08/01/10',
'/dir/2014/12/15/10',
'/dir/2015/01/01/14',
);
$dates = array();
foreach($paths as $path)
{
$matches = array();
preg_match('#([^\/]+?)\/([^\/]+?)\/([^\/]+?)\/([^\/]+?)\/([^\/]+)#', $path, $matches);
$dates[$path] = strtotime(sprintf("%s-%s-%s %s:00:00", $matches[2], $matches[3], $matches[4], $matches[5]));
}
asort($dates);
$dates = array_keys($dates);
$oldest = array_shift($dates);
$newest = array_pop($dates);
It changes date find by regex to unixtimestamp then sorts it and returns top and bottom value of sorted array.
Little bit like Pascal style)
<?php
$oldest = '';
$newest = '';
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('./dir/'));
foreach ($iterator as $file => $object) {
if ($iterator->getDepth() === 4) {
$name = $object->getPath();
if ($name > $newest) {
$newest = $name;
}
if (empty($oldest) or $name < $oldest) {
$oldest = $name;
}
}
}
var_export([$oldest, $newest]);
Result:
array (
0 => './dir/2014/03/01/08',
1 => './dir/2015/01/01/14',
)
All you have to do is loop through each folder and find the directory that has the lowest number. If you have the file paths stored in a database, it can be easier, but from your question it seems like you want to search the folders.
<?php
$base = 'dir';
$yearLowest = lowestDir($base);
$monthLowest = lowestDir($yearLowest);
$dayLowest = lowestDir($monthLowest);
echo $dayLowest;
function lowestDir($dir) {
$lowest = null;
$handle = opendir($dir);
while(($name = readdir($handle))) {
if($name == '.' || $name == '..') {
continue;
}
if(is_dir($dir.'/'.$name) && ($lowest == null || $name < $lowest)) {
$lowest = $name;
}
}
closedir($handle);
return $dir.'/'.$lowest;
}
?>
With a URI such as /test/test/test, I would like a way to create an array from $__SERVER['request_uri'] such as this:
[0] => '/' [1] => /test/ [2] => /test/test/ [3] => /test/test/test/
Any help would be appreciated!
A quick an dirty script I just created for you:
<?
$path = "/test/test2/test3";
$arr = explode("/", $path);
$arraynew = array();
$i=0;
foreach ($arr as $k => $v) {
if (($k==0) && ($v=="")) {
$arraynew[$i] = "/";
$i++;
continue;
}
if ($i == 1) {
$arraynew[$i] = $arraynew[$i-1] . $v;
} else {
$arraynew[$i] = $arraynew[$i-1] . "/" . $v;
}
$i++;
}
print_r($arraynew);
?>
Or this one that's more clean and simple and adds "/" at the end:
<?
$path = "/test/test2/test3";
$arr = explode("/", $path);
$arraynew = array();
$i=0;
foreach ($arr as $k => $v) {
$i > 0 ? $arraynew[] = $arraynew[$i-1] . $v . "/" : $arraynew[] = "/";
$i++;
}
print_r($arraynew);
?>
Look up the dirname() function.
It gives you the parent directory of any given filename or directory name.
In your case, simply use it repeatedly (in a loop) to get each successive parent folder name.
Can someone help me with some code or instructions on how to walk recursively an array and when reaching the last element print the full path to it? A simple echo will work because I will adapt the code to some other function I'm developing.
The function doesn't need to figure the array dimension because this param will be passed:
Example:
$depth = 8;
$array[1][3][5][6][9][5][8][9];
When function reachs the 8th element it print all the path to it:
//print path
'1 -> 3 -> 5 -> 6 -> 9 -> 5 -> 8 -> 9'
As I said, only printing in this format will work cause I will implement the code into some other function.
array keys can have the same value. Obviously not the same value in the same sequence for the entire arary.
Updated:
Walk recursively function:
$someArray[1][2][3] = 'end';
$someArray[1][2][6] = 'end';
$someArray[1][3][6] = 'end';
$someArray[4][3][7] = 'end';
function listArrayRecursive(&$array_name, $ident = 0){
if (is_array($array_name)){
foreach ($array_name as $k => &$v){
if (is_array($v)){
for ($i=0; $i < $ident * 10; $i++){ echo " "; }
echo $k . " : " . "<br>";
listArrayRecursive($v, $ident + 1);
}else{
for ($i=0; $i < $ident * 10; $i++){ echo " "; }
echo $k . " : " . $v . "<br>";
}
}
}else{
echo "Variable = " . $array_name;
}
}
listArrayRecursive($someArray);
Will print:
1 :
2 :
3 : end
6 : end
3 :
6 : end
4 :
3 :
7 : end
Now, how can I also print the path of the array everytime it reaches the end? For example:
1 :
2 :
3 : end : path -> 1,2,3
6 : end : path -> 1,2,6
3 :
6 : end : path -> 1,3,6
4 :
3 :
7 : end : path -> 4,3,7
EDITED CODE ADDING A THIRD PARAM TO RECORD THE PATH:
$someArray[1][2][3] = 'end';
$someArray[1][2][6] = 'end';
$someArray[1][3][6] = 'end';
$someArray[4][3][7] = 'end';
$someArray[3][2] = 'end';
function listArrayRecursive(&$array_name, $ident = 0, $path = null){
foreach ($array_name as $k => &$v){
if (is_array($v)){
for ($i=0; $i < $ident * 10; $i++){ echo " "; }
echo $k . " : " . "<br>";
$path .= $k . ', ';
listArrayRecursive($v, $ident + 1, $path);
}else{
for ($i=0; $i < $ident * 10; $i++){ echo " "; }
echo $k . " : " . $v . ' - path -> ' . $path . "<br>";
}
}
}
listArrayRecursive($someArray);
Will print:
1 :
2 :
3 : end - path -> 1, 2,
6 : end - path -> 1, 2,
3 :
6 : end - path -> 1, 2, 3,
4 :
3 :
7 : end - path -> 1, 4, 3,
3 :
2 : end - path -> 1, 4, 3,
You could employ a RecursiveIteratorIterator (docs) to take the hard work out of recursing through the arrays.
function listArrayRecursive($someArray) {
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($someArray), RecursiveIteratorIterator::SELF_FIRST);
foreach ($iterator as $k => $v) {
$indent = str_repeat(' ', 10 * $iterator->getDepth());
// Not at end: show key only
if ($iterator->hasChildren()) {
echo "$indent$k :<br>";
// At end: show key, value and path
} else {
for ($p = array(), $i = 0, $z = $iterator->getDepth(); $i <= $z; $i++) {
$p[] = $iterator->getSubIterator($i)->key();
}
$path = implode(',', $p);
echo "$indent$k : $v : path -> $path<br>";
}
}
}
This example is to give you idea, not to solve the actual task.
function recursiveSearch($array,$search){
foreach($array as $key=>$val){
if($val==$search)return $key;
$x=recursiveSearch($array[$key],$search);
if($x)return $key.' -> '.$x;
}
}
echo recursiveSearch($array,'search');
If no match is found, null is returned.
$a= array(1,2,3,4,5,6);
$val = end($a);
print_array($a,$val);
function print_array(&$arr, $val)
{
if ($val === false)
return;
$curr = prev($arr);
print_array($arr,$curr);
echo $val;
}
I just wrote a function that makes recursive looping a bit easier:
Similar to array_walk_recursive but with some extra functionality
public static function walk($array, $callback, $custom = null, $recursive = false, $info = [])
{
$r = $recursive;
if (gettype($r) === 'integer') {
$r--;
}
$info['depth'] = empty($info)?1:$info['depth'] + 1;
$info['count'] = count($array);
$info['i'] = 1;
foreach($array as $k => $v) {
if (is_array($v) && $r > 0) {
$array[$k] = static::walk($v, $callback, $custom, $r, $info);
} else {
$array[$k] = $callback($v, $k, $custom, $info);
}
$info['i'] ++;
}
return $array;
}
public static function walkable($v, $k, $custom, $info)
{
if (is_string($v)) {
return $v." [ custom: {$custom['key']} ] [ level: ".$info['depth'].' | No '.$info['i'].' of '.$info['count']." ]";
}
return $v;
}
Called like so:
$result = Namespace\ClassName::walk($array, ['Namespace\ClassName', 'walkable'], ['key'=>'value'], true);
Setting recursive to false will only evaluate the first level.
Setting recursive to true will cause it to traverse the entire array.
Setting recursive to an integer will cause it to only traverse to that depth.
Walkable functions can be referenced or passed to callback as anonymous function.
(expects: value, key, custom, info)
The returned value replace the current value.
Custom data can be passed and some additional info is provided for you.
You can expand on the walk function if you need additional info.
I had similar problem. Here is a Depth-First Search-ish solution(no path depth included, it reaches until the very end of the array). Comment the 'if' statement if u don't want to include the value:
$output = array();
retrievePath($someArray, $output);
function retrievePath($someArray, array &$pathKeeper)
{
if(!is_array($someArray)){ // $someArray == "end"
$element = array_pop($pathKeeper) ?? '';// if the array is empty pop returns null, we don't want that
array_push($pathKeeper, $element . '->'. $someArray);
} else{
end($someArray);//we want to get the last element from the array so we move the internal pointer to it's end
$endElKey = key($someArray);//take the key where the pointer is
reset($someArray);
foreach($someArray as $key=>$value){
$element = array_pop($pathKeeper);
array_push($pathKeeper, $element === null ? $key : $element . '->' . $key);// we don't want '->' at the beginning
retrievePath($value, $pathKeeper);
if($key != $endElKey) //we check whether this is not the last loop
array_push($pathKeeper, $element);
}
}
}
<?php
function printListRecursive($a, $var='', $i = 0) {
if (!is_array($a)) {
$var .= $a;
return $var;
}
$string = "";
foreach ($a as $k => $value) {
$string .= str_repeat(" ", $i) .' - '. $k . ':';
if (!is_array($value)) {
$string .= $value . '<br />';
} else {
$string .= '<br />';
$string .= printListRecursive($value, $var, $i + 1);
}
}
return $string;
}
$test_array = [
'America' => [
'Argentina' => 'Buenos Aires',
'Peru' => 'Lima'
],
'Europe' => [
'Ireland' => 'Dublin',
'France' => 'Paris',
'Italy' => 'Rome'
]
];
$result = printListRecursive($test_array);
echo $result;
?>
Check code here
I came up with the following function based on #salathe's one. It returns an array where each element is an array containing the leaf at index 0 and the array of the path keys at index 1:
function loosenMultiDimensionalArrayPathForEachVal($array) {
$iterator = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($array), \RecursiveIteratorIterator::SELF_FIRST);
$iterator->rewind();
$res = [];
foreach ($iterator as $v) {
$depth = $iterator->getDepth();
for ($path = array(), $i = 0, $z = $depth; $i <= $z; $i++) {
$path[] = $iterator->getSubIterator($i)->key();
}
$leaf = $array;
foreach ($path as $pathKey) {
$leaf = $leaf[$pathKey];
}
if (!is_array($leaf)) {
$res[] = [
$v,
$path
];
}
}
return $res;
}
The main reason I implemented this one is that $iterator->hasChildren() returns true if the current iterated leaf is an object. Therefore, I wouldn't be able to get the path of it that way.
I found this solution, which also keeps into account if elements of the structure are arrays:
$file_contents=file_get_contents("data.json");
$json_dump=json_decode($file_contents);
printPath($json_dump, '', "" ,"");
function printPath($the_array, $path, $prevType) {
// Parse all elements of a structure
// and print full PHP path to each one.
foreach($the_array as $key => $value) {
if(is_array($value)) {
// Array element cannot be directly printed: process its items as objects:
printPath($value, $path . $key , "array");
} else {
if (!is_object($value)) { // If the element is not an object, it can be printed (it's a leaf)
if(is_string($value)) {
$finalValue = '"' . $value . '"';
} else {
$finalValue = $value;
}
if($prevType == "array") {
// If array element, add index in square brackets
echo($path . "[" . $key . "] = " . $finalValue . "<br>");
} else {
echo($path . $key . " = " . $finalValue . "<br>");
}
} else { // else store partial path and iterate:
if($prevType == "array") {
// Path of array element must contain element index:
printPath($value, $path . "[" . $key . "]->" , "dummy");
} else {
printPath($value, $path . $key . "->", "dummy");
}
}
}
}
}
Example output:
status->connections->DSS-25->band = "X"
status->connections->DSS-25->endAt = "2019-11-20T20:40:00.000Z"
status->connections->DSS-25->startAt = "2019-11-20T12:40:00.000Z"
geometry[0]->obs[0]->name = "UDSC64"
geometry[0]->obs[0]->hayabusa2->azm = 90.34
geometry[0]->obs[0]->hayabusa2->alt = -20.51
In case anybody is interested, here it is the port to Javascript:
function iterate(obj, stack, prevType) {
for (var property in obj) {
if ( Array.isArray(obj[property]) ) {
//console.log(property , "(L=" + obj[property].length + ") is an array with parent ", prevType, stack);
iterate(obj[property], stack + property , "array");
} else {
if ((typeof obj[property] != "string") && (typeof obj[property] != "number")) {
if(prevType == "array") {
//console.log(stack + "[" + property + "] is an object, item of " , prevType, stack);
iterate(obj[property], stack + "[" +property + "]." , "object");
} else {
//console.log(stack + property , "is " , typeof obj[property] , " with parent ", prevType, stack );
iterate(obj[property], stack + property + ".", "object");
}
} else {
if(prevType == "array") {
console.log(stack + "[" + property + "] = "+ obj[property]);
} else {
console.log(stack + property , " = " , obj[property] );
}
}
}
}
}
iterate(object, '', "File")
You can add a third parameter which holds the actual path as String. At the end you can output it then.
I have an array from a $_GET say
Array
(
[0] => 0
[1] => 1
[2] => 2
[3] => 3
[4] => 4
)
for which i am using this while loop to create a string:
while (list($key, $value) = each($_GET)) {
$get_url .= $key . '=' . rawurlencode(stripslashes($value)) . '&';
}
Now if i get an array from $_GET say like:
Array
(
[0] => pid
[1] => gid
[2] => Array
(
[0] => 0
[1] => 1
[2] => 2
[3] => 3
[4] => 4
)
)
then in this case what could be the possible changes be done to while loop so that I can avoid the result like this http://www.example.com/shopping_cart.php?0=pid&1=gid&2=Array in url when i use this to redirect it.
I want the url to display the values properly..not like "2=Array".. how can i do this?
EDIT
Thanks Folks for the help, but I cannot introduce new function neither I can replace the while loop with for loop, I would be very thankfull if you can help me in re-editing the given WHILE Loop...
EDIT 2
I am using header(location:$get_url) for redirecting to created url, is this the problem of display of "2=Array" in url?
EDIT 3
functions used to build query, NOTE: THESE FUNCTIONS ARE INBUILT FUNCTION OF osCommerce
I still changed one of it by introducing the foreach loop into it see below the use and function definition:
function tep_redirect($url) {
if ( (strstr($url, "\n") != false) || (strstr($url, "\r") != false) ) {
tep_redirect(tep_href_link(FILENAME_DEFAULT, '', 'NONSSL', false));
}
if ( (ENABLE_SSL == true) && (getenv('HTTPS') == 'on') ) { // We are loading an SSL page
if (substr($url, 0, strlen(HTTP_SERVER . DIR_WS_HTTP_CATALOG)) == HTTP_SERVER . DIR_WS_HTTP_CATALOG) { // NONSSL url
$url = HTTPS_SERVER . DIR_WS_HTTPS_CATALOG . substr($url, strlen(HTTP_SERVER . DIR_WS_HTTP_CATALOG)); // Change it to SSL
}
}
$url = str_replace("&", "&", $url);
header('Location: ' . $url);
tep_exit();
}
=========================
function tep_href_link($page = '', $parameters = '', $connection = 'NONSSL', $add_session_id = true, $search_engine_safe = true) {
global $request_type, $session_started, $SID, $spider_flag;
if (!tep_not_null($page)) {
die('</td></tr></table></td></tr></table><br><br><font color="#ff0000">' . TEP_HREF_LINK_ERROR1);
}
if ($connection == 'NONSSL') {
$link = HTTP_SERVER . DIR_WS_HTTP_CATALOG;
} elseif ($connection == 'SSL') {
if (ENABLE_SSL == true) {
$link = HTTPS_SERVER . DIR_WS_HTTPS_CATALOG;
} else {
$link = HTTP_SERVER . DIR_WS_HTTP_CATALOG;
}
} else {
die('</td></tr></table></td></tr></table><br><br><font color="#ff0000">' . TEP_HREF_LINK_ERROR2);
}
if (tep_not_null($parameters)) {
while ( (substr($parameters, -5) == '&') ) $parameters = substr($parameters, 0, strlen($parameters)-5);
$link .= $page . '?' . tep_output_string($parameters);
$separator = '&';
} else {
$link .= $page;
$separator = '?';
}
// if session is not started or requested not to add session, skip it
if ( ($add_session_id == true) && ($session_started == true) ){
// if cookies are not set and not forced, then add the session info incase the set cookie fails
if ( ! isset($_COOKIE[tep_session_name()]) && (SESSION_FORCE_COOKIE_USE == 'False') ) {
$_sid = tep_session_name() . '=' . tep_session_id();
// if we are chaning modes and cookie domains differ, we need to add the session info
} elseif ( HTTP_COOKIE_DOMAIN . HTTP_COOKIE_PATH != HTTPS_COOKIE_DOMAIN . HTTPS_COOKIE_PATH
&&
(
( $request_type == 'NONSSL' && $connection == 'SSL' && ENABLE_SSL == true )
||
( $request_type == 'SSL' && $connection == 'NONSSL' )
)
) {
$_sid = tep_session_name() . '=' . tep_session_id();
}
}
if (isset($_sid) && !$spider_flag) {
$link .= $separator . tep_output_string($_sid);
}
return $link;
}
===========================
function tep_get_all_get_paramtrs($exclude_array = '') {
global $HTTP_GET_VARS;
if (!is_array($exclude_array)) $exclude_array = array();
$get_url = '';
if (is_array($HTTP_GET_VARS) && (sizeof($HTTP_GET_VARS) > 0))
{
reset($HTTP_GET_VARS);
foreach($HTTP_GET_VARS as $key => $a)
{
if(is_array($a))
{
foreach($a as $k => $v)
{
$get_url[] = $key . '[]' . '=' . rawurlencode(stripslashes($v));
}
}
else
{
$get_url[] = $key . '=' . rawurlencode(stripslashes($a));
}
}
/* while (list($key, $value) = each($HTTP_GET_VARS))
{
if(!is_array($value))
{
if ( (strlen($value) > 0) && ($key != tep_session_name()) && ($key != 'error') && (!in_array($key, $exclude_array)) && ($key != 'x') && ($key != 'y') )
{
$get_url .= $key . '=' . rawurlencode(stripslashes($value));
}
}
else
{
if ( (strlen($value) > 0) && ($key != tep_session_name()) && ($key != 'error') && (!in_array($key, $exclude_array)) && ($key != 'x') && ($key != 'y') )
{
$get_url .= preg_replace('/#\d/','[]',http_build_query($value,$key.'#'));
}
/* while(list($key1, $value1) = each($value))
{
if ( (strlen($value1) > 0) && ($key1 != tep_session_name()) && ($key1 != 'error') && (!in_array($key1, $exclude_array)) && ($key1 != 'x') && ($key1 != 'y') )
{
$get_url .= $key1 . '=' . rawurlencode(stripslashes($value1));
}
}*/
/* }
}*/
$get_url .= '&';
}
return $get_url;
}
========================
tep_redirect(tep_href_link($goto, tep_get_all_get_paramtrs($parameters)));
here $parameters is an array with two values which doesnt have any resemblence with url display logic
did you consider using http_build_query() or http_build_url()?
If you want to create an url from a multi-dimensional array, you should use a recursion, or just the built-in php function, which results the same as the function I created http-build-query() (just as Maurice Kherlakian said). It's the easiest way for doing this.
A recursive function example:
function URLfromArray($array,$url = "")
{
foreach($array as $key => $val)
{
if(is_array($val))
{
$url = URLfromArray($val,$url);
}
else
{
$url .= $key."=".$val."&";
}
}
return $url;
}
You could check with is_array and use implode or just loop through the array if you want different keys such as 2_0, 2_1.
You can check if the value is an array, then recurse or iterate over it...
if (is_array($value)
{
// process this array
}
else
{
// normal path
$get_url .= $key . '=' . rawurlencode(stripslashes($value)) . '&';
}
http://php.net/manual/en/function.is-array.php
header() has nothing to do with your problem.
you just fail to build proper query string
And I wonder why your problem is still persists despite of all these http_build_query() you've been told already
You could use serialize to serialize the array into a single string and urlencode that resulting string, on the receiving end you would then use urldecode and unserialize.