Saving directory name and folder size as 2d array - php

I use this code to print the subfolders name and size in a HTML table, what I want to do is save this table as a 2d array so it can be sorted by folder size (since right now is sorted by alphabetical name order)
<?php
$directory = "F:/directory";
echo "<table>";
$depth = 0;
$count = 0;
$ritit = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST);
$r = array();
foreach ( $ritit as $splFileInfo ) {
$count +=1;
if ($ritit->getDepth() === $depth && $splFileInfo->isDir()) {
echo "<tr><td>".stripslashes($splFileInfo)."</td>";
echo "<td>".getSize($splFileInfo)."</td></tr>";
}
}
echo "</table>";
function getSize($dir, $precision = 2) {
$ritit = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS));
$bytes = 0;
foreach ( $ritit as $v ) {
$bytes += $v->getSize();
}
$bytes = max($bytes, 0);
return round($bytes, $precision) . ' ';
}
?>
This is the HTML with the folder name on the left and the folder size on the right

Store the filename as the key and the filesize as the value in $r 2D-array. Use the sort function to the sort the array using value.
Here is the link to sort the array: https://www.w3schools.com/php/php_arrays_sort.asp
<?php
$directory = "F:/directory";
echo "<table>";
$depth = 0;
$count = 0;
$ritit = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST);
$r = array();
foreach ( $ritit as $splFileInfo ) {
$count +=1;
if ($ritit->getDepth() === $depth && $splFileInfo->isDir()) {
// echo "<tr><td>".stripslashes($splFileInfo)."</td>";
// echo "<td>".getSize($splFileInfo)."</td></tr>";
$r[stripslashes($splFileInfo)] = getSize($splFileInfo);
}
}
// sort the associative array using the value.
asort($r);
foreach($r as $key => $value) {
echo "<tr><td>".$key."</td>";
echo "<td>".$value."</td></tr>";
}
echo "</table>";
function getSize($dir, $precision = 2) {
$ritit = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS));
$bytes = 0;
foreach ( $ritit as $v ) {
$bytes += $v->getSize();
}
$bytes = max($bytes, 0);
return round($bytes, $precision) . ' ';
}
?>

Related

How to list subfolders of a folder inside zip file without extracting?

I'm using php ziparchive function to open zip file and want to list all subfolders of a folder located in a zip file.
For example...
World.zip contains a folder country,foo & bar and I want to list names of all states folder of that country folder,but not of foo & bar
i found this note on the manuals which i got most of the code from
which depends on looping through the total number of files
and i refactored it to match your needs and here is the code after refactoring
<?php
$filePath = 'zip/file.zip';
$za = new ZipArchive();
if ($za->open($filePath) !== true) { // check for the zip archive
echo "archive doesn't exist or it's on Read-only mode ";
} else {
$Tree = $pathArray = array(); //empty arrays
for ($i = 0; $i < $za->numFiles; $i++) {
$path = $za->getNameIndex($i);
$pathBySlash = array_values(explode('/', $path));
$c = count($pathBySlash);
$temp = &$Tree;
for ($j = 0; $j < $c - 1; $j++)
if (isset($temp[$pathBySlash[$j]]))
$temp = &$temp[$pathBySlash[$j]];
else {
$temp[$pathBySlash[$j]] = array();
$temp = &$temp[$pathBySlash[$j]];
}
if (substr($path, -1) == '/')
$temp[$pathBySlash[$c - 1]] = array();
else
$temp[] = $pathBySlash[$c - 1];
}
$array = $Tree['folder_name_to_list_its_files'];
// First style of Displaying
echo "<pre>";
print_r($array);
// Second style of Displaying
foreach ($array as $key => $value) {
foreach ($value as $val) {
echo $key . " | " . $val . "<br /> \n";
}
}
echo "</pre>";
}

PHP FTP recursive directory listing

I'm trying to make a recursive function to get all the directories and sub directories from my ftp server in an array.
I tried a lot of functions I've found on the web. The one that works best for me is this one:
public function getAllSubDirFiles() {
$dir = array(".");
$a = count($dir);
$i = 0;
$depth = 20;
$b = 0;
while (($a != $b) && ($i < $depth)) {
$i++;
$a = count($dir);
foreach ($dir as $d) {
$ftp_dir = $d . "/";
$newdir = ftp_nlist($this->connectionId, $ftp_dir);
foreach ($newdir as $key => $x) {
if ((strpos($x, ".")) || (strpos($x, ".") === 0)) {
unset($newdir[$key]);
} elseif (!in_array($x, $dir)) {
$dir[] = $x;
}
}
}
$b = count($dir);
}
return $dir ;
}
The problem with this function is it wont allow the directory to have a "." in it's name and every file that is located in the root directory will be considered a directory as well. So I adjusted the function and got this:
public function getAllSubDirFiles($ip, $id, $pw) {
$dir = array(".");
$a = count($dir);
$i = 0;
$depth = 20;
$b =0;
while (($a != $b) && ($i < $depth)) {
$i++;
$a = count($dir);
foreach ($dir as $d) {
$ftp_dir = $d . "/";
$newdir = ftp_nlist($this->connectionId, $ftp_dir);
foreach ($newdir as $key => $x) {
if (!is_dir('ftp://'.$id.':'.$pw.'#'.$ip.'/'.$x)) {
unset($newdir[$key]);
} elseif (!in_array($x, $dir)) {
$dir[] = $x;
}
}
}
$b = count($dir);
}
return $dir ;
}
This works pretty good but and gives the result I want. but it's so slow it's unusable.
I also tried working with ftp_rawlist but it has the same drawback of being horribly slow.
public function getAllSubDirFiles() {
$dir = array(".");
$a = count($dir);
$i = 0;
$depth = 20;
$b = 0;
while (($a != $b) && ($i < $depth)) {
$i++;
$a = count($dir);
foreach ($dir as $d) {
$ftp_dir = $d . "/";
$newdir = $this->getFtp_rawlist('/' . $ftp_dir);
foreach ($newdir as $key => $x) {
$firstChar = substr($newdir[$key][0], 0, 1);
$a = 8;
while ($a < count($newdir[$key])) {
if ($a == 8) {
$fileName = $ftp_dir . '/' . $newdir[$key][$a];
} else {
$fileName = $fileName . ' ' . $newdir[$key][$a];
}
$a++;
}
if ($firstChar != 'd') {
unset($newdir[$key]);
} elseif (!in_array($fileName, $dir)) {
$dir[] = $fileName;
}
}
}
$b = count($dir);
}
return $dir;
}
public function getFtp_rawlist($dir) {
$newArr = array();
$arr = ftp_rawlist($this->connectionId, $dir);
foreach ($arr as $value) {
$stringArr = explode(" ", $value);
$newArr[] = array_values(array_filter($stringArr));
}
return $newArr;
}
I've been stuck on this problem for the last couple of days and I'am getting desperate. If any one has any suggestion please let me know
If your server supports MLSD command and you have PHP 7.2 or newer, you can use ftp_mlsd function:
function ftp_mlsd_recursive($ftp_stream, $directory)
{
$result = [];
$files = ftp_mlsd($ftp_stream, $directory);
if ($files === false)
{
die("Cannot list $directory");
}
foreach ($files as $file)
{
$name = $file["name"];
$filepath = $directory . "/" . $name;
if (($file["type"] == "cdir") || ($file["type"] == "pdir"))
{
// noop
}
else if ($file["type"] == "dir")
{
$result = array_merge($result, ftp_mlsd_recursive($ftp_stream, $filepath));
}
else
{
$result[] = $filepath;
}
}
return $result;
}
If you do not have PHP 7.2, you can try to implement the MLSD command on your own. For a start, see user comment of the ftp_rawlist command:
https://www.php.net/manual/en/function.ftp-rawlist.php#101071
If you cannot use MLSD, you will particularly have problems telling if an entry is a file or folder. While you can use the ftp_size trick, calling ftp_size for each entry can take ages.
But if you need to work against one specific FTP server only, you can use ftp_rawlist to retrieve a file listing in a platform-specific format and parse that.
The following code assumes a common *nix format.
function ftp_nlst_recursive($ftp_stream, $directory)
{
$result = [];
$lines = ftp_rawlist($ftp_stream, $directory);
if ($lines === false)
{
die("Cannot list $directory");
}
foreach ($lines as $line)
{
$tokens = preg_split("/\s+/", $line, 9);
$name = $tokens[8];
$type = $tokens[0][0];
$filepath = $directory . "/" . $name;
if ($type == 'd')
{
$result = array_merge($result, ftp_nlst_recursive($ftp_stream, $filepath));
}
else
{
$result[] = $filepath;
}
}
return $result;
}
For DOS format, see: Get directory structure from FTP using PHP.
I've build an OOP FTP Client library that's can help you on this a lot, using just this code you can retrieve a list of only the directories with addition useful information like (chmod, last modified time, size ...).
The code :
// Connection
$connection = new FtpConnection("localhost", "foo", "12345");
$connection->open();
// FtpConfig
$config = new FtpConfig($connection);
$config->setPassive(true);
$client = new FtpClient($connection);
$allFolders =
// directory, recursive, filter
$client->listDirectoryDetails('/', true, FtpClient::DIR_TYPE);
// Do whatever you want with the folders
This code a variation of Martin Prikryl one. It is slower but do not have any failures with whitespaces. Use this code only if you have any problems with the code above.
function ftp_list_files_recursive($ftp_stream, $path){
$lines = ftp_nlist($ftp_stream, $path);
$result = array();
foreach ($lines as $line) {
if (ftp_size($ftp_stream, $line) == -1) {
$result = array_merge($result, ftp_list_files_recursive($ftp_stream, $line));
}
else{
$result[] = $line;
}
}
return $result;
}

How do I put this array into csv or excel?

I can't figure out a way to do this and I really bad with working with arrays so I hope someone can help me.
I have array $stuck that just contains names, like this
$stuck = array('Daniel','Alex','alfredo','dina');
I am using sort to put the names in alphabetical order, like this
sort($stuck);
Now the thing is I want to put this array into a csv or excel file so the first letter of name will be the title and all the names with this letter will be under this title.
This is an example of what I am trying to accomplish
Here try this. I added comments between the code.
$csvArray = array();
$nameArray = array('Daniel','Alex','alfredo','dina');
//Create an array that can be used for creating the CSV
foreach($nameArray as $name)
{
$firstLetter = strtoupper(substr($name, 0,1));
$csvArray[$firstLetter][] = $name;
}
//Determine the subarray which have the must rules
$maxRuleCount = 0;
foreach($csvArray as $firstLetter => $nameArray)
{
if(count($nameArray) > $maxRuleCount)
{
$maxRuleCount = count($nameArray);
}
}
//Create the first rule (letters)
$csvContent = implode(';', array_keys($csvArray));
$csvContent .= "\r\n";
//Loop through the CSV array and create rules of the names
for($i = 0 ; $i < $maxRuleCount ; $i++)
{
foreach($csvArray as $firstLetter => $nameArray)
{
$csvContent .= #$csvArray[$firstLetter][$i].';';
}
$csvContent .= "\r\n";
}
//The hole csv content is in the $csvContent variable now
//If you want to print it you need to add the text/plain header because of the \r\n new line characters
header("Content-Type:text/plain");
echo $csvContent;
Ouput is:
D;A
Daniel;Alex;
dina;alfredo;
<?php
$stuck = array('Daniel','Alex','Dina','durban','eigor','alfredo','dina');
// associate names with first letters in a new array
foreach ($stuck as $name) {
$first_letter = strtoupper( substr( $name, 0, 1 ) );
$new_name_array[ $first_letter ][] = $name;
}
// store the first letter list in an array and sort it
$first_letters = array_keys( $new_name_array );
sort( $first_letters );
// sort the name lists
foreach( array_keys( $new_name_array ) as $letter ) {
sort( $new_name_array[$letter] );
}
// output csv header row
echo "'".implode( "','", $first_letters )."'\n";
// output the CSV name rows
$i = 0;
while ( true ) {
$row = array();
$is_row = false;
foreach( $first_letters as $letter ) {
$row[] = $new_name_array[ $letter ][ $i ];
if( ! empty( $new_name_array[ $letter ][ $i ] ) ) $is_row = true;
}
if( ! $is_row ) exit;
echo "'" . implode( "','", $row ) . "'\n";
$i++;
}
?>
Outputs quoted CSV:
'A','D','E'
'Alex','Daniel','eigor'
'alfredo','Dina',''
'','dina',''
'','durban',''
<?php
$stuck = array('Daniel','Alex','alfredo','dina', 'eigor', 'durban');
sort($stuck);
$sortedNames = array();
$maxEl = 0;
foreach ($stuck as $name) {
$name = strtolower($name);
$sortedNames[$name{0}][] = $name;
$maxEl = count($sortedNames[$name{0}]) < $maxEl ? $maxEl : count($sortedNames[$name{0}]);
}
$headers = array_keys($sortedNames);
$finalArray = array($headers);
for($i = 0; $i < $maxEl; $i++) {
$tmpRow = array();
foreach ($sortedNames as $names) {
$tmpRow[] = isset($names[$i]) ? $names[$i] : '';
}
$finalArray[] = $tmpRow;
}
$file = fopen("names.csv","w");
foreach ($finalArray as $line)
{
fputcsv($file,$line);
}
fclose($file);
Here is my attempt at this question:
$names = array('Daniel','Alex','alfredo','dina', 'eigor', 'falfoul', 'fiona');
natcasesort($names);
$columns = array();
foreach ($names as $name) {
$first = strtoupper(substr($name, 0, 1));
$columns[$first][] = $name;
}
// pad the columns so they all have the same number of rows
$maxRows = 0;
foreach ($columns as &$col) {
$numRows = count($col);
if ( $numRows > $maxRows) {
$maxRows = $numRows;
}
}
// pad the columns
foreach ($columns as &$column) {
$column = array_pad($column, $maxRows, '');
}
$firstLetter = array_keys($columns);
$numCols = count($firstLetter);
// transpose the columns array
$rows = array();
foreach ($columns as $csvCol) {
foreach ($csvCol as $k=>$n) {
$rows[$k][] = $n;
}
}
// pad the rows to ensure they all have the same number of
// columns
foreach ($rows as &$row) {
$row = array_pad($row, $numCols, '');
}
// add the title row to the rows
array_unshift($rows, $firstLetter);
$handle = fopen("names.csv", 'w');
foreach ($rows as $fRow) {
fputcsv($handle, $fRow);
}

Hiding columns from PHP array output

I have a code that pulls data from a .txt file and puts it in an HTML table. The way the code is written it pulls more data than I need. How can I hide columns from the output? This is returning 9 columns where I only need the first 5.
<?php
function toASCII($str) {
return strtr(utf8_decode($str), utf8_decode(
'ŠŒŽšœžŸ¥µÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿ'), 'SOZsozYYuAAAAAAACEEEEIIIIDNOOOOOOUUUUYsaaaaaaaceeeeiiiionoooooouuuuyy');
}
require_once 'meekrodb.php';
DB::$user = 'root';
DB::$password = '';
DB::$dbName = 'test';
$dirArray = array();
$dirpath = 'C:\xampp\htdocs\auditscripts\SCRIPTS\\';
$SERVERNAME=array();
// open this directory
$myDirectory = scandir($dirpath);
// get each entry
foreach ($myDirectory as $value) {
if (is_dir($dirpath . $value)) {
$myDirectory = scandir($dirpath . $value);
if (is_file($dirpath . $value . '\secedit_mergedpolicy.txt')){
$dirArray[] = $dirpath . $value . '\secedit_mergedpolicy.txt';
$servername[]=$value;
}
}
}
// count elements in array
$indexCount = count($dirArray);
Print ("$indexCount files<br>\n");
// sort 'em
sort($dirArray);
// print 'em
print("<TABLE border=1 cellpadding=5 cellspacing=0 class=whitelinks>\n");
print("<TR><TH>Server Name</TH><th>VALUE01</th><th>VALUE02</th><th>VALUE03</th><th>VALUE04</th><th>VALUE05</th></TR>\n");
// loop through the array of files and print them all
for ($index = 0; $index < $indexCount; $index++) {
if (substr("$dirArray[$index]", 0, 1) != ".") { // don't list hidden files
$data_tisert = array();
print("<TR><TD>$servername[$index]</td>");
$myString = file_get_contents($dirArray[$index]);
$l1 = explode('<br />', nl2br($myString));
$st = false;
foreach ($l1 as $vx) {
if (!strpos($vx,'=')){
$st=false;
}
if ($st) {
$l1_1 = explode('=', $vx);
print("<TD>$l1_1[1]</td>");
$data_tisert[] = $l1_1[1];
}
/// echo trim(utf8_encode($vx)) == '[Event Audit]' ? 'y' : 'n';
if (utf8_encode(trim($vx)) == utf8_encode('[Event Audit]')) {
$st = true;
}
}
DB::insert('event_audit', array(
'VALUE01' => $data_tisert[0],
'VALUE02' => $data_tisert[1],
'VALUE03' => $data_tisert[2],
'VALUE04' => $data_tisert[3],
'VALUE05' => $data_tisert[4],
));
print("</TR>\n");
}
}
print("</TABLE>\n");
?>
if you only want 5, just put a counter on your array:
Instead of:
foreach ($l1 as $vx) {
Use
for ($ii=0; ( ($ii < 5) && ($ii < count($l1)) ); $ii++){
$vx = $l1[$ii];

Remove empty array elements from the $_FILES array

I have a photo album, with a 'photos' field in database which has serialized data and is base 64 encoded. I can upload up to 21 photos at the same time for the album (multiple uploading). When trying to add new photos to the album on edit mode, I can't get the new $_FILES array to merge with the old array in the database. I want to update only the values that I have changed. So let's say I already had 2 images inside my album, I would like to add a 3rd image without losing the other 2 images. I know I need to use unset() to delete empty values but I think i'm not doing it correctly. Here's my code:
if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'edit') {
//select photo album and get photos and descriptions to be merged with new.
$album_id = $_REQUEST['album_id'];
$old_photos = null;
$old_descriptions = null;
$getAlbumQ = mysql_query("select * from albums where id='$album_id'");
while ($old_album = mysql_fetch_array($getAlbumQ)) {
$old_photos = unserialize(base64_decode($old_album['photos']));
$old_descriptions = unserialize(base64_decode($old_album['descriptions']));
}
if (isset($_POST['album_name']) && isset($_POST['desc'])) {
$name = $_POST['album_name'];
$desc = $_POST['desc'];
$idesc = array();
$target_path = "../uploads/albums/";
foreach ($_FILES as $k => $v) {
//first upload photos
$path = $target_path . basename($v['name']);
if(move_uploaded_file($v['tmp_name'], $path)) {
$hasUpload = true;
}
}
for ($j = 1; $j < 21; $j++) {
$img_index = $j;
$img_desc = $_POST['desc_' . $img_index];
$asdf = $_FILES['image_' . $img_index]['name'];
array_push($idesc, $img_desc);
}
foreach( $_FILES['images']['name'] as $key => $value ) {
if( empty($value) ) {
unset( $_FILES['images']['name'][$key] );
}
}
for ($i = 1; $i < 21; $i++) {
if($_FILES['image_'.$i]['name']!= '')
{
$hasUpload = true;
$presults = array_merge($old_photos, $_FILES); //THE PROBLEM WITH MERGING ARRAYS OCCURS HERE
$dresults = array_merge($old_descriptions, $idesc);
$images = base64_encode(serialize($presults));
$descriptions = base64_encode(serialize($dresults));
$posted = date("Y-m-d H:i:s");
}
else {
$hasUpload = false;
$presults = $old_photos;
$dresults = $idesc;
$images = base64_encode(serialize($presults));
$descriptions = base64_encode(serialize($dresults));
$posted = date("Y-m-d H:i:s");
}
}
}
}
I tried something similar to what Muu suggested. Combined it with another piece of code of a custom merge function found here http://keithdevens.com/weblog/archive/2003/Mar/30/MergeTwoArrays and it worked fine! Thanks for your help
Just needed to add the following lines of code outside the for ($i = 1; $i < 21; $i++) loop:
//delete empty values for $_FILES array
foreach ($_FILES as $key => $value) {
foreach ($value as $k => $v) {
if ($k=='name' && $v!='') {
break;
} else {
unset($_FILES[$key]);
}
}
}
//custom array merge function
function merge(&$a, &$b){
$keys = array_keys($a);
foreach($keys as $key){
if(isset($b[$key])){
if(is_array($a[$key]) and is_array($b[$key])){
merge($a[$key],$b[$key]);
}else{
$a[$key] = $b[$key];
}
}
}
$keys = array_keys($b);
foreach($keys as $key){
if(!isset($a[$key])){
$a[$key] = $b[$key];
}
}
}
then instead of array_merge(), I used merge() inside the for ($i = 1; $i < 21; $i++) loop:
$presults = merge($old_photos, $_FILES);
Instead of removing the empty values from the array, why not just ignore them?
For example:
$nonEmptyArray = array();
foreach ($_FILES as $k => $v) {
if (!$v) {
continue;
}
$nonEmptyArray[$k] = $v;
}
$_FILES = $nonEmptyArray;

Categories