<?php

include 'wp-load.php';
include ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';

// If you want to dump out $fs or $fs_map
// ini_set( 'xdebug.var_display_max_depth', 50 );
// ini_set( 'xdebug.var_display_max_children', -1 );

class WP_Filesystem_Tester extends WP_Filesystem_Base {
	private $cwd;

	// Holds a array of objects which contain an array of objects, etc.
	private $fs = null;

	// Holds a array of /path/to/file.php and /path/to/dir/ map to an object in $fs above
	// a fast more efficient way of determining if a path exists, and access to that node
	private $fs_map = array();

	var $verbose = true;

	function __construct( $system_prefix = '', $paths = '' ) {
		$this->fs = (object)array(
			'name' => '/', // The "name" of this entry. root is special in that it includes the slash, all others do not
			'type' => 'd', // d = directory, f = file
			'path' => '/', // Full path to entry, primarily for ::cwd()
			'children' => array(), // Any children entries of this directory, unset for files
		);
		$this->fs_map[ '/' ] = $this->fs;
		$this->cwd = $this->fs;
		$this->setfs( $system_prefix, $paths );
	}

	function setfs( $system_prefix, $paths ) {
		if ( ! is_array($paths) )
			$paths = explode( "\n", $paths );

		$paths = array_filter( array_map( 'trim', $paths ) );

		foreach ( $paths as $path ) {

			$prefixless = $system_prefix ? preg_replace( '!^' . preg_quote( untrailingslashit( $system_prefix ), '!' ) . '!i', '', $path ) : $path;

			if ( '/' == $path[ strlen($prefixless) -1 ] )
				$this->mkdir( $prefixless );
			else
				$this->put_contents( $prefixless );
		}

	}

	function mkdir( $path ) {
		$p = array_filter( explode( '/', $path ) );
		$parent = $this->fs;
		foreach ( $p as $i => $dir ) {
			if ( ! isset( $parent->children[ $dir ] ) ) {
				$full_path = '/' . implode( '/', array_slice( $p, 0, $i ) ) . '/';
				$parent->children[ $dir ] = (object) array(
					'name' => $dir,
					'type' => 'd',
					'path' => $full_path,
					'children' => array(),
				);
				$this->fs_map[ $full_path ] = $parent->children[ $dir ];
			}
			$parent = $parent->children[ $dir ];
		}
		return $parent;
	}

	function put_contents( $file, $contents = '', $mode = null ) {
		$parent = $this->mkdir( dirname( $file ) );

		$parent->children[ basename( $file ) ] = (object)array(
			'name' => basename( $file ),
			'type' => 'f',
			'path' => $file,
			'contents' => $contents,
		);
		$this->fs_map[ $file ] = $parent->children[ basename( $file ) ];
	}

	function get_contents( $file ) {
		if ( ! $this->is_file( $file ) )
			return false;
		return $this->fs_map[ $file ]->contents;
	}

	function cwd() {
		return $this->cwd->path;
	}
	function chdir( $path ) {
		//var_dump( [ __METHOD__, $path ] );
		if ( ! isset( $this->fs_map[ $path ] ) )
			return false;

		$this->cwd = $this->fs_map[ $path ];
		return true;
	}
	function exists( $path ) {
		//var_dump( [ __METHOD__, $path, isset( $this->fs_map[ $path ] ) ] );
		return isset( $this->fs_map[ $path ] );
	}
	function is_file( $file ) {
		//var_dump( [ __METHOD__, $file ] );
		if ( ! isset( $this->fs_map[ $file ] ) )
			return false;

		return ( 'f' == $this->fs_map[ $file ]->type );
	}
	function is_dir( $path ) {
		//var_dump( [ __METHOD__, $path ] );
		if ( ! isset( $this->fs_map[ $path ] ) )
			return false;

		return ( 'd' == $this->fs_map[ $path ]->type );
	}
	
	function dirlist( $path = '.', $include_hidden = true, $recursive = false ) {

		if ( empty( $path ) || '.' == $path )
			$path = $this->cwd();

		if ( ! isset( $this->fs_map[ $path ] ) )
			return false;

		$limit_file = false;
		if ( $this->is_file( $path ) ) {
			$limit_file = basename( $path );
			$path = dirname( $path ) . '/';
		}

		$ret = array();
		foreach ( $this->fs_map[ $path ]->children as $entry ) {
			if ( '.' == $entry->name || '..' == $entry->name )
				continue;
	
			if ( ! $include_hidden && '.' == $entry->name )
				continue;
	
			if ( $limit_file && $entry->name != $limit_file )
				continue;

			$struc = array();
			$struc['name'] = $entry->name;
			$struc['type'] = $entry->type;
	
			if ( 'd' == $struc['type'] ) {
				if ( $recursive )
					$struc['files'] = $this->dirlist( trailingslashit( $path ) . trailingslashit( $struc['name'] ), $include_hidden, $recursive );
				else
					$struc['files'] = array();
			}
	
			$ret[ $entry->name ] = $struc;
		}
		return $ret;
	}


}

$ftp_root_prefix = '/www/example.com';
$paths = array(
	'/www/example.com/example.com/plugins/', // OK
	'/www/example.com/wp.example.com/wp-content/plugins/', // FAIL
//	'/www/example.com/wp.example.com/wp-content/plugins/hello.php', // Testing Example only
);
$fs = new WP_Filesystem_Tester( $ftp_root_prefix, $paths );

foreach ( $paths as $path ) {
	$expected = preg_replace( '!^' . preg_quote( untrailingslashit( $ftp_root_prefix ), '!' ) . '!i', '', $path );
	$actual = $fs->find_folder( $path );
	var_dump( compact( 'path', 'expected', 'actual' ) );
}
