Search code examples
phplinuxpopensmb

Counting files on a shared windows drive via Php from Linux


I have files with .msg extension on a windows shared folder and my php server is Linux (LAMP server). I am trying to write a php script which simply counts the number of .msg files on the Windows shared folder.

I am using smbclient class and this is what I wrote:

<?php
require_once ('smbclient.php');
$smbc = new smbclient ('//192.168.10.14/reservations', 'user', 'pass');
$handle = popen ($smbc);
$files = glob($handle . '*.msg');
$filecount = count( $files );
echo $filecount;
?>

However, I am always getting 0 as output, but there are over 200 files.


Solution

  • You can't glob a handle like that. You are effectively trying to glob Resource (12)/*.msg if it is an actual resource returned by popen (meaning that smbclient::__toString() would need to return protocol://username:password@host/the/share/url and would have needed to automagically registered a stream wrapper for protocol).

    But even then it wouldn't work because glob only works with things existent in the filesystem (so it would need to actually be mounted)... Seems to also be the case with SPL's GlobIterator.

    At a minimum, you will need to traverse every file and check the name against your pattern. So keep in mind any solution at this point is going to be somewhat slow depending on the network connection and number of files/dirs on the share.

    Since I don't know the code for the smb client you implementation you are using ill give you an example with one I do know how to use, and that works. munkie/samba is a PHP SMB Client and corresponding stream wrapper for system smbclient so you will need to use filesystem functions that work with streams to utilize it:

    Using just PHP (and SPL, which should be built in)

    Using SPL iterators we can make short work of recursively reading directories and searching against filenames:

    // assuming you installed via composer so use its autoloader
    $autoloader = require_once('vendor/autoload.php');
    
    use Samba\SambaStreamWrapper;
    
    SambaStreamWrapper::register();
    
    $dir = new RecusrsiveDirectoryIterator(new DirectoryIterator(
        'smb://username:[email protected]/reservations'
    ));
    
    $finder = new RegexIterator($dir, '#^.+\.msg$#i', RecursiveRegexIterator::MATCH);
    
    $count = 0;
    
    foreach($finder as $match) {
       $count++;
    }
    
    echo $count;
    

    Using the VERY handy symfony/finder component

    The symfony/finder component makes things a bit easier on us and less cryptic. In addition to supporting globs and regex search patterns it implements Countable so we can call $var->count() to get the count instead of looping over the results and counting them manually (though internally it still needs to iterate over the result to count). It also makes much more complex searches easier to work with. Doesn't sound like that is support you need at the moment, but it might come in hand later:

    // assuming you installed via composer so use its autoloader
    $autoloader = require_once('vendor/autoload.php');
    
    use Samba\SambaStreamWrapper;
    use Symfony\Component\Finder\Finder;
    
    SambaStreamWrapper::register();
    
    $finder = new Finder();
    $finder
        ->files()
        ->name('*.msg')
        ->in('smb://username:[email protected]/reservations');
    
    // YAY Countable!
    echo $finder->count();
    

    The other alternative you have here is to actually mount the share and then use glob(), GlobIterator, or symfony/finder. But that can get a bit tricky depending on the nature of what you are using this for and how you want to go about mounting it. It's been delved into a bit here.

    Finally, if these are email messages in a mailbox you are probably much better off using a mailbox library/component like Zend_Mail to connect and count the messages.