Listing Subfolders

This basic function called listFolders() is working well enough and builds upon an existing function that I’ve been using for quote a while called buildPath(). BuildPath() which I included for reference is simply fed a root folder name or a comma-separated list of the root folder and subfolder(s) and it builds a full or relative path to it depending on the parameters being passed. All this is working on my test site until I realized that the test site has no subfolders under any of the others so as a test I created one and found that it doesn’t show. The listFolder() function calls the folders inside the one being passed into it but no subfolders within those and I’m not sure how to proceed. Any ideas?

<?php

// BUILD PATH FROM INPUT VALUES
// $systemFolder IS A FOLDER OR COMMA-SEPARATED LIST CONTAINING THE FOLDER(S) TO USE
// $removefolder WHEN TRUE (1) REMOVES THE SITE'S ROOT FOLDER
// $relative WHEN TRUE (1) SHOWS THE PATH TO THE SITE'S ROOT FOLDER
// $trailing IF TRUE (1) ADDS A TRAILING SLASH TO THE PATH
// $common SPECIFIES IF FOLDER IS OUTSIDE THE SITE FILE SYSTEM
function listFolders($systemFolder, $valueType="", $removefolder=0, $relative=0, $trailing=1, $common=0) {
	$folderArray = (is_array($systemFolder)) ? [$systemFolder] : explode(',', $systemFolder);

	// CONSTRUCT ABSOLUTE PATH FROM FOLDER(S) SPECIFIED
	$systemPath = buildPath($folderArray, $removefolder, 0, 1, $common);

	// ADD TRAILING SLASH IF SECIFIED
	$trailing = ($trailing > 0) ? DIRECTORY_SEPARATOR : "";

	// INITIALIZE ARRAY TO STORE FOLDER NAMES
	$folders = [];

	// SCAN THE CONSTRUCTED DIRECTORY
	$items = scandir($systemPath);

	if (is_array($items)) :
		if (is_dir($systemPath)) :
			foreach ($items as $item) :
				// IGNORE DOT, OLD, OBSOLETE AND BAK FOLDERS
				if (str_contains($item, '.')) continue;
				if (str_contains($item, '..')) continue;
				if (str_contains($item, 'OBSOLETE')) continue;
				if (str_contains($item, 'OLD')) continue;
				if (str_contains($item, 'BAK')) continue;

				// GENERATE PATH BASED ON VALUES PASSED
				$path = buildPath($folderArray, $removefolder, $relative, $trailing, $common);

				// BUILD VARIABLE NAME
				$varname =  ucfirst($item) . $valueType;

				// REBUILD THE PATH FOR OUTPUT
				$path = $path . DIRECTORY_SEPARATOR . $item . $trailing;

				// BUILD ARRAY WITH FOLDER NAME AS KEY AND FULL PATH AS VALUE
				$folders[$varname] = $path;
			endforeach;
			return $folders;
		endif;
	endif;
}

 function buildPath($folders, $removefolder=0, $relative=1, $trailing=1, $common=0) {
	$folders = (!is_array($folders)) ? [$folders] : $folders;

	if (is_array($folders)) :
		// DEFINE VARIABLES
		$directoryseparator = DIRECTORY_SEPARATOR;
		$trailing		= ($trailing > 0) ? $directoryseparator : "";
		$path			= $_SERVER['DOCUMENT_ROOT'] . $directoryseparator . join($directoryseparator, $folders) . $trailing;
		$sitepath		= $_SERVER['DOCUMENT_ROOT']; // /var/www/html/test.loc

		// DEFINE SITE DOMAIN NAME
		$sitefolder	= $_SERVER['SERVER_NAME']; // text.loc

		// CREATE PATH BASED ON VALUES PASSED TO FUNCTION
		$path	= ($common > 0) ? str_replace("$sitefolder/","",$path): $path;
		$path	= ($removefolder > 0 && $relative > 0) ? str_replace("$sitepath","",$path) : $path;
		$path	= ($removefolder > 0) ? str_replace("$sitefolder/","",$path): $path;
		$path	= ($relative > 0) ? str_replace($sitepath,"",$path): $path;
		return $path;
	endif;
}

// CALLED USING
listFolders("internals", $valueType="", 0, 0, 1, 0); // LIST internals FOLDERS

// EXAMPLE OF OUTPUT
/* 
Array
		(
			[Configuration] => /var/www/html/test.loc/internals/configuration/
			[Functions] => /var/www/html/test.loc/internals/functions/
			[Includes] => /var/www/html/test.loc/internals/includes/
			[Styles] => /var/www/html/test.loc/internals/styles/
		)
 */
?>

Your function code needs to call itself, recursively, when any $item is a folder, supplying the current path as $systemFolder input parameter, and use the returned value, appended to the current path, to build the path you want to display.

Thank you. I’ve never called a function within itself so Ill have to see what it entails.

I added another call within a second foreach loop but it’s not quite working so clearly something is amiss in the syntax or values used.

The error is on the new foreach loop call but not sure why the error as there should be output from the function call (re-call) but this time on any sub-directories. There are not always sub-directories, though, so could that be the issue?

Warning: foreach() argument must be of type array|object, null given so with the implication that it is not getting the type of data it needs, I added square brackets around the function call which no longer gives errors but it also does not show sub-directories.

function listFolders($systemFolder, $valueType='Absolute', $removefolder=0, $relative=0, $trailing=1, $common=0) {
	$folderArray = (is_array($systemFolder)) ? [$systemFolder] : explode(',', $systemFolder);

	// CONSTRUCT ABSOLUTE PATH FROM FOLDER(S) SPECIFIED
	$systemPath = buildPath($folderArray, $removefolder, 0, 1, $common);

	// ADD TRAILING SLASH IF SPECIFIED
	$trailing = ($trailing > 0) ? DIRECTORY_SEPARATOR : "";

	// INITIALIZE ARRAY TO STORE FOLDER NAMES
	$folders = [];

	if (is_dir($systemPath)) :
		// SCAN THE CONSTRUCTED DIRECTORY
		$items = scandir($systemPath);
		if (is_array($items)) :
			foreach ($items as $item) :

				// IGNORE DOT, OBSOLETE, BAK FOLDERS AND ALL FILES
				if (str_contains($item, '.')) continue;
				if (str_contains($item, '..')) continue;
				if (str_contains($item, 'OBSOLETE')) continue;
				if (str_contains($item, 'OLD')) continue;
				if (str_contains($item, 'BAK')) continue;

				// GENERATE PATH BASED ON VALUES PASSED
				$path = buildPath($folderArray, $removefolder, $relative, $trailing, $common);

				// SCAN THE FOLDER AGAIN FOR SUBFOLDERS		
				if (is_dir($path)) :
					foreach (listFolders($path, $removefolder, $relative, $trailing, $common) as $subpath) :
						$folders[] = $path . DIRECTORY_SEPARATOR . $subpath . $trailing;
					endforeach;
				else :
					$folders[] = $path;
				endif;

				// BUILD VARIABLE NAME
				$varname =  ucfirst($item) . $valueType;

				// REBUILD THE PATH FOR OUTPUT
				$path = $path . DIRECTORY_SEPARATOR . $item . $trailing;

				// BUILD ARRAY WITH FOLDER NAME AS KEY AND FULL PATH AS VALUE
				$folders[$varname] = $path;
			endforeach;
			return $folders;
		endif;
	endif;
}

The function should always - return $folders; at the very end of the code, so that if there are no sub-folders in the current path, an empty array will get returned.

The first parameter you are suppling to the recursive listFolders() call, $path, isn’t even based on the current $item value being looped over.

I haven’t done much programming since retiring decades ago so I don’t quite follow what you mean. The function returns $folders at the end which uses $path as the value to build the array. I understand the error as $path in each outer loop is the current item being looped over and I do understand that by the time $path is called again from the function in the second foreach it is not an array but rather a string value of the path but I would expect that the function being called again would produce an array from it, I don’t know at all what it is I need to do.

Sponsor our Newsletter | Privacy Policy | Terms of Service