PHP script to automatically create file structure representation [duplicate] - php

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
PHP - Iterate through folders and display HTML contents
Using PHP, I'm trying to create a script that will navigate to the root directory of a website, and from there, using scandir() or glob() (those being the only directory scanning functions I've learned), I would use a recursive method to navigate through all the items in the root directory, then re-calling itself when encountering an entry that tested to be a directory, through is_dir($fileName).
Here's where I'm encountering problems - when I reach an entry that is a directory, it correctly navigates the if statement correctly to the commands for directories, but when calling itself, I don't seem to be able to get the glob() directory right, since every time I call it, the page ceases to load anything more. I'm trying to figure out, from the relative URL-based nature of scanning directories how I would reference it. I set a variable $ROOT_DIR, which is the root directory relative to the directory in which the php page is located in (in this case, $ROOT_DIR="../../"), and then I'd think logically, I'd call scanAllFiles [my sitemap method] with $ROOT_DIR . $fileName, where that's the string of the directory found, after removing the leading "../../" from the string. After trying this, it doesn't work.
Should I be using a different directory-traversing method to do this, or am I formatting the method call incorrectly?

Most people just use MySQL to make sitemaps, doing it manually.
Exposing files isn't safe, but you can add some security.
<?php
function files($dir=".") {
$blacklist = array(str_replace("/","",$_SERVER['SCRIPT_NAME']), 'admin.php', 'users.txt', 'secret.txt');
$return = array();
$glob1 = glob($dir."/*");
for($i=0;$i<=count($glob1)-1;$i++) {
$item = $glob1[$i];
$nodir = str_replace($dir, "", $item);
if(is_dir($item)) {
$file1 = explode('/', $item);
$file = $file1[count($file1)-1];
$merge = array_merge($return, files($item));
if(!in_array($file, $blacklist) and !empty($nodir)) $return = $merge;
}
else {
$file1 = explode('/', $item);
$file = $file1[count($file1)-1];
if(!in_array($file, $blacklist) and !empty($nodir)) $return[] = str_replace("./","",$item);
}
}
return $return;
}
// Use like this:
$files = files(); // Get all files from top folder down, no traling slash ...
for($i=0;$i<=count($files)-1;$i++) { // ... Go through them ...
echo "<li>$files[$i]</li>"; // ... And echo the item
}
?>

Related

Create .tar.gz file using PHP

The project I am working on requires creating .tar.gz archives and feeding it to an external service. This external service works only with .tar.gz so another type archive is out of question. The server where the code I am working on will execute does not allow access to system calls. So system, exec, backticks etc. are no bueno. Which means I have to rely on pure PHP implementation to create .tar.gz files.
Having done a bit of research, it seems that PharData will be helpful to achieve the result. However I have hit a wall with it and need some guidance.
Consider the following folder layout:
parent folder
- child folder 1
- child folder 2
- file1
- file2
I am using the below code snippet to create the .tar.gz archive which does the trick but there's a minor issue with the end result, it doesn't contain the parent folder, but everything within it.
$pd = new PharData('archive.tar');
$dir = realpath("parent-folder");
$pd->buildFromDirectory($dir);
$pd->compress(Phar::GZ);
unset( $pd );
unlink('archive.tar');
When the archive is created it must contain the exact folder layout mentioned above. Using the above mentioned code snippet, the archive contains everything except the parent folder which is a deal breaker for the external service:
- child folder 1
- child folder 2
- file1
- file2
The description of buildFromDirectory does mention the following so it not containing the parent folder in the archive is understandable:
Construct a tar/zip archive from the files within a directory.
I have also tried using buildFromIterator but the end result with it also the same, i.e the parent folder isn't included in the archive. I was able to get the desired result using addFile but this is painfully slow.
Having done a bit more research I found the following library : https://github.com/alchemy-fr/Zippy . But this requires composer support which isn't available on the server. I'd appreciate if someone could guide me in achieving the end result. I am also open to using some other methods or library so long as its pure PHP implementation and doesn't require any external dependencies. Not sure if it helps but the server where the code will get executed has PHP 5.6
Use the parent of "parent-folder" as the base for Phar::buildFromDirectory() and use its second parameter to limit the results only to "parent-folder", e.g.:
$parent = dirname("parent-folder");
$pd->buildFromDirectory($parent, '#^'.preg_quote("$parent/parent-folder/", "#").'#');
$pd->compress(Phar::GZ);
I ended up having to do this, and as this question is the first result on google for the problem here's the optimal way to do this, without using a regexp (which does not scale well if you want to extract one directory from a directory that contains many others).
function buildFiles($folder, $dir, $retarr = []) {
$i = new DirectoryIterator("$folder/$dir");
foreach ($i as $d) {
if ($d->isDot()) {
continue;
}
if ($d->isDir()) {
$newdir = "$dir/" . basename($d->getPathname());
$retarr = buildFiles($folder, $newdir, $retarr);
} else {
$dest = "$dir/" . $d->getFilename();
$retarr[$dest] = $d->getPathname();
}
}
return $retarr;
}
$out = "/tmp/file.tar";
$sourcedir = "/data/folder";
$subfolder = "folder2";
$p = new PharData($out);
$filemap = buildFiles($sourcedir, $subfolder);
$iterator = new ArrayIterator($filemap);
$p->buildFromIterator($iterator);
$p->compress(\Phar::GZ);
unlink($out); // $out.gz has been created, remove the original .tar
This allows you to pick /data/folder/folder2 from /data/folder, even if /data/folder contains several million OTHER folders. It then creates a tar.gz with the contents all being prepended with the folder name.

PHP is_dir defective

Strange behaviour, exentially:
(the name of the folder depends on the date - the purpose is a hit counter of the website, broken down by day)
if (!is_dir($folder)) { // first access in the day
mkdir($folder);
}
Well: on the server in internet all works well.
But when i try in local, with the server simulator of Easy PHP, happens that:
(a) The first time, no problem. The folder doesn't exists and it is created.
(b) subsequently, for example to a page refresh, the program flow again goes in the IF (!!!) generating the error (at line of mkdir) of kind: "Warning: mkdir(): No such file or directory in [...]".
All parent part of the directory $folder exists.
Thanks
.
Try using a recursive directory creation function:
function mkdir_r($dirName, $rights = 0777)
{
$dirs = explode(DIRECTORY_SEPARATOR , $dirName);
$dir = '';
if (strpos($dirs[count($dirs) - 1], '.')) {
array_pop($dirs);
}
foreach ($dirs as $part) {
$dir .= $part . DIRECTORY_SEPARATOR ;
if (!is_dir($dir) && strlen($dir) > 0) {
mkdir($dir, $rights);
}
}
}
This way all directories up to the directry you wanted to create are created if they don't exist.
mkdir doesn't work recursively unfortunately.
If anyone faces the issue; Use the native clearstatcache() function after you delete the file.
I'm quoting the interesting part of the original documentation
You should also note that PHP doesn't cache information about non-existent files. So, if you call file_exists() on a file that doesn't exist, it will return false until you create the file. If you create the file, it will return true even if you then delete the file. However unlink() clears the cache automatically.
For further information here is the documentation page: https://www.php.net/manual/en/function.clearstatcache.php

PHP scandir - multiple directories

I am creating a WordPress plugin which allows a user to apply sorting rules to a particular template (page, archive, single etc). I am populating list of pages using PHP scandir like so:
$files = scandir(get_template_directory());
The problem is that I keep single.php templates in a '/single' subfolder so these templates are not being called by the above function.
How can I use multiple directories within the scandir function (perhaps an array?) or will I need a different solution?
So basically I am trying to:
$files = scandir( get_template_directory() AND get_template_directory().'/single' );
My current solution (not very elegant as it requires 2 for each loops):
function query_caller_is_template_file_get_template_files()
{
$template_files_list = array();
$files = scandir(get_template_directory());
$singlefiles = scandir(get_template_directory().'/single');
foreach($files as $file)
{
if(strpos($file, '.php') === FALSE)
continue;
$template_files_list[] = $file;
}
foreach($singlefiles as $singlefile)
{
if(strpos($file, '.php') === FALSE)
continue;
$template_files_list[] = $singlefile;
}
return $template_files_list;
}
First, there's not really anything wrong about what you're doing. You have two directories, so you do the same thing twice. Of course you could make it look a little cleaner and avoid the blatant copy paste:
$files = array_merge(
scandir(get_template_directory()),
scandir(get_template_directory().'/single')
);
Now just iterate over the single array.
In your case, getting the file list recursively doesn't make sense, as there may be subdirectories you don't want to check. If you did want to recurse into subdirectories, opendir() and readdir() along with is_dir() would allow you to build a recursive scan function.
You could event tighten up the '.php' filter part a bit with array_filter().
$files = array_filter($files, function($file){
return strpos($file, '.php');
});
Here I'm assuming that should a file start with .php you're not really interested in it making your list (as strpos() will return the falsy value of 0 in that case). I'm also assuming that you're sure there will be no files that have .php in the middle somewhere.
Like, template.php.bak, because you'll be using version control for something like that.
If however there is the chance of that, you may want to tighten up your check a bit to ensure the .php is at the end of the filename.

How to get a file name based on last modified in php? [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
get last modified file in a dir?
I have lot of file inside a folder. How can I get the last modified file name using php?
Is it possible? kindly help me.Thanks in advance.
Yes, it is possible.
Please refer this method: http://php.net/manual/en/function.filemtime.php
In order to work list all files in a folder an get the filemtime. Compare them and you will find the latest updated file in that folder.
Remember that, this method will return in UNIX time() but not a date like (YYYY/mm/dd)
UPDATE:
Finding most new file is directly is not possible. You have to check files by one by.
<?php
function findMostNewFile($folder) {
$files = array();
foreach (scandir($folder) as $file) {
$path = $folder . DIRECTORY_SEPARATOR . $file;
if (is_dir($path)) continue; // do not count folders. only files.
$files[filemtime($path)] = $path;
}
krsort($files);
$arr = array_slice($files, 0, 1); // return the first newest file's path as array
return $arr[0]; // return only a path and name as a string
}
?>
Thats all.
UPDATE: return as string.
I'm pretty sure you'll have to look through all files and grab the max mod time. Use 'glob' to loop through files and use filemtime() to grab the file mod date/time.

Find and replace in multiple files

OK, whats the best solution in php to search through a bunch of files contents for a certain string and replace it with something else.
Exactly like how notepad++ does it but obviously i dont need the interface to that.
foreach (glob("path/to/files/*.txt") as $filename)
{
$file = file_get_contents($filename);
file_put_contents($filename, preg_replace("/regexhere/","replacement",$file));
}
So I recently ran into an issue in which our web host converted from PHP 5.2 to 5.3 and in the process it broke our installation of Magento. I did some individual tweaks that were suggested, but found that there were still some broken areas. I realized that most of the problems were related to an issue with the "toString" function present in Magento and the now deprecated PHP split function. Seeing this, I decided that I would try to create some code that would find and replace all the various instances of the broken functions. I managed to succeed in creating the function, but unfortunately the shot-gun approach didn't work. I still had errors afterwards. That said, I feel like the code has a lot of potential and I wanted to post what I came up with.
Please use this with caution, though. I'd recommended zipping a copy of your files so that you can restore from a backup if you have any issues.
Also, you don't necessarily want to use this as is. I'm providing the code as an example. You'll probably want to change what is replaced.
The way the code works is that it can find and replace whatever is in the folder it is put in and in the sub folders. I have it tweaked so that it will only look for files with the extension PHP, but you could change that as needed. As it searches, it will list what files it changes. To use this code save it as "ChangePHPText.php" and upload that file to wherever you need the changes to happen. You can then run it by loading the page associated with that name. For example, mywebsite.com\ChangePHPText.php.
<?php
## Function toString to invoke and split to explode
function FixPHPText( $dir = "./" ){
$d = new RecursiveDirectoryIterator( $dir );
foreach( new RecursiveIteratorIterator( $d, 1 ) as $path ){
if( is_file( $path ) && substr($path, -3)=='php' && substr($path, -17) != 'ChangePHPText.php'){
$orig_file = file_get_contents($path);
$new_file = str_replace("toString(", "invoke(",$orig_file);
$new_file = str_replace(" split(", " preg_split(",$new_file);
$new_file = str_replace("(split(", "(preg_split(",$new_file);
if($orig_file != $new_file){
file_put_contents($path, $new_file);
echo "$path updated<br/>";
}
}
}
}
echo "----------------------- PHP Text Fix START -------------------------<br/>";
$start = (float) array_sum(explode(' ',microtime()));
echo "<br/>*************** Updating PHP Files ***************<br/>";
echo "Changing all PHP containing toString to invoke and split to explode<br/>";
FixPHPText( "." );
$end = (float) array_sum(explode(' ',microtime()));
echo "<br/>------------------- PHP Text Fix COMPLETED in:". sprintf("%.4f", ($end-$start))." seconds ------------------<br/>";
?>

Categories