PHP iterating directories with ftp_ functions - php

Ok guys so heres another debug nightmare that I've come across and can't seem to figure out exactly what is going on here -_-
Basically what is happening is that this code returns JSON to my ajax request. Which involves originally an array from the php.
array(
"public_html"=>
array(
"public_html"=>
array(//...other files actually in public html),
),
0 =>
array(
"filename":".ftpquota",
"filesize": 12kb
),
);
So as you can see it's creating a public_html index then adding another with the same name with the actual files inside public_html this is not what I want, I want the original public_html to come back as the files in public_html
/*
Iterates over files and directories recursively
*/
function ftpFileList($ftpConnection, $path="/") {
//create general array to pass back later
$files = array();
//grabs the contents of $path
$contents = ftp_nlist($ftpConnection, $path);
//explode the $path to get correct index to fill in
$secondaryPath = explode('/',$path);
//test if the last index in the array is blank
//if it is pop it from the array
if($secondaryPath[count($secondaryPath) - 1] == "")
array_pop($secondaryPath);
//if the array is larger than or equal to one
if(count($secondaryPath) >= 1){
//we will rewrite $secondaryPath to the last index (actual file name)
$secondaryPath = $secondaryPath[count($secondaryPath) - 1];
}else{
//if it isn't anything we'll make it the path that we originally passed
$secondaryPath = $path;
}
//check for contents
if($contents){
//iterate over the contents
foreach($contents as $currentFile) {
//if the current file is not . or .. we don't need that at all
if($currentFile !== "." && $currentFile !== ".."){
//if there is a period in the currentFile this means it's a file not a directory
if( strpos($currentFile,".") == 0 ) {
if($files[""]){
if($secondaryPath == $currentFile)
$files[""][$secondaryPath][] = ftpFileList($ftpConnection, $path.$currentFile.'/');
else
$files[""][$secondaryPath][$currentFile] = ftpFileList($ftpConnection, $path.$currentFile.'/');
}else{
$files[$secondaryPath][] = ftpFileList($ftpConnection,$path.$currentFile.'/');
}
}else{
//here we know the $currentFile is a file
if($currentFile !== "." && $currentFile !== ".."){
//file in the correct index with a new index which is an array with information
$files[$secondaryPath][] = array(
"file"=>$currentFile,//file name
"filesize"=>humanFileSize(ftp_size($ftpConnection,"/".$path.$currentFile)),//human readable file size
"creation_date"=>date("F d Y g:i:sa",ftp_mdtm($ftpConnection,"/".$path.$currentFile)),//date in a human readable format
"full_path"=>"/".$path.$currentFile//full path of the file so we can access this later
);
}
}
}
}
return $files;//return the array
}
}
I'm hoping this is partially easy to understand, I made a bunch of notes for debugging purposes. I need to iterate and get the files correctly in a nice JSON file so I can iterate over that with JS. It's hard to debug this with the internet I have and testing it in the IDE I am using, I only have a chromebook. (if anyone knows of a good app to use for the chromebook. )

Related

How to display latest 4 images from mounted network folder in php?

I have used symlinks to fetch mounted network folder in the specified path "/var/www/Access/images".As the beginner tried certain piece of code but it is displaying broken image.I think there is issue in path format,but unable to figure out.Please any one suggest.
`<?php
// Open the directory
$dir = opendir('/var/www/Access/images');
// Initialize an array to store the file names and last modified times
$files = array();
// Read the contents of the directory
while (($file = readdir($dir)) !== false) {
// Exclude the current and parent directories
if ($file == '.' || $file == '..') continue;
// Check if the file is a directory
if (is_dir("/var/www/Access/images/'$file")) continue;
// Get the last modified time for the file
$lastModified = filemtime("/var/www/html/Access/images/'$file");
// Add the file name and last modified time to the array
$files[$file] = $lastModified;
}
// Close the directory
closedir($dir);
// Sort the array by last modified time
asort($files);
$files=array_reverse($files);
// Initialize a counter
$count = 0;
// Iterate through the sorted array
foreach ($files as $file => $lastModified) {
// Display the image
echo "<img src='/var/www/Access/images/'$file'>";
// Increment the counter
$count++;
// Stop displaying images after the fourth one
if ($count == 2) break;
}
?>`
``
browser can not recognize path except the root of web server.
for example if /var/www/project/ is your source containg index.php that web server load it , browser can read data only in this directory and subdirectory such /var/www/project/access/images/img.png
to set src attribute of <img> you can accomplish this using:
absoulte path : https://example.com/access/images/img.png
relative path : access/images/img.png
I hope this works

Delete files not matching a list

So I'm trying to make a simple script, it will have a list of predefined files, search for anything that's not on the list and delete it.
I have this for now
<?php
$directory = "/home/user/public_html";
$files = glob($directory . "*.*");
foreach($files as $file)
{
$sql = mysql_query("SELECT id FROM files WHERE FileName='$file'");
if(mysql_num_rows($sql) == 0)
unlink($directory . $file);
}
?>
However, I'd like to avoid the query so I can run the script more often (there's about 60-70 files, and I want to run this every 20 seconds or so?) so how would I embedd a file list into the php file and check against that instead of database?
Thanks!
You are missing a trailing / twice.. In glob() you are giving /home/user/public_html*.* as the argument, I think you mean /home/user/public_html/*.*.
This is why I bet nothing matches the files in your table..
This won't give an error either because the syntax is fine.
Then where you unlink() you do this again.. your argument home/user/public_htmltestfile.html should be home/user/public_html/testfile.html.
I like this syntax style: "{$directory}/{$file}" because it's short and more readable. If the / is missing, you see it immediately. You can also change it to $directory . "/" . $file, it you prefer it. The same goes for one line conditional statements.. So here it comes..
<?php
$directory = "/home/user/public_html";
$files = glob("{$directory}/*.*");
foreach($files as $file)
{
$sql = mysql_query("SELECT id FROM files WHERE FileName=\"{$file}\";");
if(mysql_num_rows($sql) == 0)
{
unlink("{$directory}/{$file}");
}
}
?>
EDIT: You requested recursion. Here it goes..
You need to make a function that you can run once with a path as it's argument. Then you can run that function from inside that function on subdirectories. Like this:
<?php
/*
ListDir list files under directories recursively
Arguments:
$dir = directory to be scanned
$recursive = in how many levels of recursion do you want to search? (0 for none), default: -1 (for "unlimited")
*/
function ListDir($dir, $recursive=-1)
{
// if recursive == -1 do "unlimited" but that's no good on a live server! so let's say 999 is enough..
$recursive = ($recursive == -1 ? 999 : $recursive);
// array to hold return value
$retval = array();
// remove trailing / if it is there and then add it, to make sure there is always just 1 /
$dir = rtrim($dir,"/") . "/*";
// read the directory contents and process each node
foreach(glob($dir) as $node)
{
// skip hidden files
if(substr($node,-1) == ".") continue;
// if $node is a dir and recursive is greater than 0 (meaning not at the last level or disabled)
if(is_dir($node) && $recursive > 0)
{
// substract 1 of recursive for ever recursion.
$recursive--;
// run this same function again on itself, merging the return values with the return array
$retval = array_merge($retval, ListDir($node, $recursive));
}
// if $node is a file, we add it to the array that will be returned from this function
elseif(is_file($node))
{
$retval[] = $node;
// NOTE: if you want you can do some action here in your case you can unlink($node) if it matches your requirements..
}
}
return $retval;
}
// Output the result
echo "<pre>";
print_r(ListDir("/path/to/dir/",1));
echo "</pre>";
?>
If the list is not dynamic, store it in an array:
$myFiles = array (
'some.ext',
'next.ext',
'more.ext'
);
$directory = "/home/user/public_html/";
$files = glob($directory . "*.*");
foreach($files as $file)
{
if (!in_array($file, $myFiles)) {
unlink($directory . $file);
}
}

Search and replace entire directory file contents

I need to re-write multiple files in a single directory based on the contents of a single CSV file.
For example the CSV file would contain something like this:
define("LANG_BLABLA", "NEW");
In one of the files in the directory it would contain this:
define("LANG_BLABLA", "OLD");
The script will search through the directory and any occurrences where the CSV "LANG_BLABLA" matches the old directory LANG it will update the "OLD" with the "NEW"
My question is how exactly can I list the contents of the files in the directory in 1 array so I can easily search through them and replace where necessary.
Thanks.
Searching through a directory is relatively easy:
<?
clearstatcache();
$folder = "C:/web/website.com/some/folder";
$objects = scandir($folder, SCANDIR_SORT_NONE);
foreach ($objects as $obj) {
if ($obj === '.' || $obj === '..')
continue; // current and parent dirs
$path = "{$folder}/{$obj}";
if (strcasecmp(substr($path, -4), '.php') !== 0)
continue // Not a PHP file
if (is_link($path))
$path = realpath($path);
if ( ! is_file($path))
continue; // Not a file, probably a folder
$data = file_get_contents($path);
if ($data === false)
die('Some error occured...')
// ...
// Do your magic here
// ...
if (file_put_contents($path, $data) === false)
die('Failed to write file...');
}
As for modifying PHP files dynamically, it is probably a sign that you need to put that stuff into a database or in-memory data-store... MySQL, SQLite, MongoDB, memcached, Redis, etc. should do. Which you should use would depend on the nature of your project.
You can parse a CSV file into an array using fgetcsv http://php.net/manual/en/function.fgetcsv.php
First of all I would not recommend this workflow if you working with .php files. Try to centralize your define statements and then change it in a single location.
But here is a solution that should work for you csv files. It's not complete, you have to add some of your desired logic.
/**
* Will return an array with key value coding of your csv
* #param $defineFile Your file which contains multiple definitions e.g. define("LANG_BLABLA", "NEW");\n define("LANG_ROFL", "LOL");
* #return array
*/
public function getKeyValueArray($defineFile)
{
if (!file_exists($defineFile)) {
return array();
} else {
$fp = #fopen($defineFile, 'r');
$values = explode("\n", fread($fp, filesize($defineFile)));
$newValues = array();
foreach ($values as $val) {
preg_match("%.*\"(.*)?\",\s+\"(.*)?\".*%", $val, $matches);
$newValues[$matches[1]] = $matches[2];
}
}
}
/**
* This is s stub! You should implement the rest yourself.
*/
public function updateThings()
{
//Read your definition into an array
$defs=$this->getKeyValueArray("/some/path/to/your/file");
$scanDir="/your/desired/path/with/input/files/";
$otherFiles= scandir($scanDir);
foreach($otherFiles as $file){
if($file!="." && $file!=".."){
//read in the file definition
$oldDefinitionArray=$this->getKeyValueArray($scanDir.$file);
//Now you have your old file in an array e.g. array("LANG_BLABLA" => "OLD")
//and you already have your new file in $defs
//You now loop over both and check for each key in $defs
//if its value equals the value in the $oldDefinitionArray.
//You then update your csv or rewrite or do whatever you like.
}
}
}

PHP: How can I grab a single file from a directory without scanning entire directory?

I have a directory with 1.3 Million files that I need to move into a database. I just need to grab a single filename from the directory WITHOUT scanning the whole directory. It does not matter which file I grab as I will delete it when I am done with it and then move on to the next. Is this possible? All the examples I can find seem to scan the whole directory listing into an array. I only need to grab one at a time for processing... not 1.3 Million every time.
This should do it:
<?php
$h = opendir('./'); //Open the current directory
while (false !== ($entry = readdir($h))) {
if($entry != '.' && $entry != '..') { //Skips over . and ..
echo $entry; //Do whatever you need to do with the file
break; //Exit the loop so no more files are read
}
}
?>
readdir
Returns the name of the next entry in the directory. The entries are returned in the order in which they are stored by the filesystem.
Just obtain the directories iterator and look for the first entry that is a file:
foreach(new DirectoryIterator('.') as $file)
{
if ($file->isFile()) {
echo $file, "\n";
break;
}
}
This also ensures that your code is executed on some other file-system behaviour than the one you expect.
See DirectoryIterator and SplFileInfo.
readdir will do the trick. Check the exampl on that page but instead of doing the readdir call in the loop, just do it once. You'll get the first file in the directory.
Note: you might get ".", "..", and other similar responses depending on the server, so you might want to at least loop until you get a valid file.
do you want return first directory OR first file? both? use this:
create function "pickfirst" with 2 argument (address and mode dir or file?)
function pickfirst($address,$file) { // $file=false >> pick first dir , $file=true >> pick first file
$h = opendir($address);
while (false !== ($entry = readdir($h))) {
if($entry != '.' && $entry != '..' && ( ($file==false && !is_file($address.$entry)) || ($file==true && is_file($address.$entry)) ) )
{ return $entry; break; }
} // end while
} // end function
if you want pick first directory in your address set $file to false and if you want pick first file in your address set $file to true.
good luck :)

How to compare two file structures in PHP?

I have a function which gives me the complete file structure upto n-level,
function getDirectory($path = '.', $ignore = '') {
$dirTree = array ();
$dirTreeTemp = array ();
$ignore[] = '.';
$ignore[] = '..';
$dh = #opendir($path);
while (false !== ($file = readdir($dh))) {
if (!in_array($file, $ignore)) {
if (!is_dir("$path/$file")) {
//display of file and directory name with their modification time
$stat = stat("$path/$file");
$statdir = stat("$path");
$dirTree["$path"][] = $file. " === ".
date('Y-m-d H:i:s', $stat['mtime']) . " Directory ==
".$path."===". date('Y-m-d H:i:s', $statdir['mtime']) ;
} else {
$dirTreeTemp = getDirectory("$path/$file", $ignore);
if (is_array($dirTreeTemp))$dirTree =
array_merge($dirTree, $dirTreeTemp);
}
}
}
closedir($dh);
return $dirTree;
}
$ignore = array('.htaccess', 'error_log', 'cgi-bin', 'php.ini', '.ftpquota');
//function call
$dirTree = getDirectory('.', $ignore);
//file structure array print
print_r($dirTree);
Now here my requirement is , I have two sites
The Development/Test Site- where i do
testing of all the changes
The Production Site- where I finally
post all the changes as per test in
development site
Now, for example, I have tested an image upload in the Development/test site, and i found it appropriate to publish on Production site then i will completely transfer the Development/Test DB detail to Production DB, but now I want to compare the files structure as well to transfer the corresponding image file to Production folder.
There could be the situation when I update the image by editing the image and upload it with same name, now in this case the image file would be already present there, which will restrict the use of "file_exist" logic, so for these type of situations....HOW CAN I COMPARE THE TWO FILE STRUCTURE TO GET THE SYNCHRONIZATION DONE AS PER REQUIREMENT??
EDITED
the requirement has to be a script, which I am going to need as a joomla component functionality.. please reply as per this.
I would suggest using rsync for this.

Categories