13

I'm working on some Magento 2 extension that requires reading files from the file system.
When running the php sniffer using the ECGM2 standards, it complains about the fact that I'm using functions like basename or dirname.

The use of function dirname() is forbidden

or

The use of function basename() is forbidden

What wrapper should I use instead of those to get the the same effect?

[EDIT]
Here is some code, but it's not that relevant to the question.
I have a collection class that extends the \Magento\Framework\Data\Collection\Filesystem class and I want to list this collection in a grid (ui-components) and one of the actions in the grid is a download action.
For this, I need to get the actual name of the file so I can send it to the download action.

    // here $file is dynamic and it can be
    // folder/filename.xml or folder/subfolder/file.tar.gz
    //so there is no strict number of folders and subfolders.
    $file = $downloader->getRelativePath($packageName);
    $relativeFile = UmcFilesystem::VAR_DIR_NAME . '/' .$file;
    $absoluteFile = $rootDir->getAbsolutePath($relativeFile);
    if ($rootDir->isFile($relativeFile) && $rootDir->isReadable($relativeFile)){
        //I don't want to use `explode` just for the sake of avoiding basename
        $fileName = basename($absoluteFile);
        $this->fileFactory->create(
            $fileName,
            null,
            DirectoryList::VAR_DIR,
            'application/octet-stream',
            $rootDir->stat($relativeFile)['size']
        );

        $resultRaw = $this->resultRawFactory->create();
        $resultRaw->setContents($rootDir->readFile($relativeFile));
        return $resultRaw;
    } else {
       ...
    }
4
  • can you share part of your code, what you have try to reading file from system. Commented Nov 8, 2016 at 11:26
  • I added some code, but it's totally irrelevant for the question. The question is somehow abstract. what should I use instead of basename so that the code sniffer does not complain?
    – Marius
    Commented Nov 8, 2016 at 12:14
  • Its look like permission issue only. Commented Nov 8, 2016 at 12:26
  • It has nothing to do with permissions. The code works properly, but the code sniffer says that should not use basename in there. Please read the question carefully.
    – Marius
    Commented Nov 8, 2016 at 12:31

4 Answers 4

28
+50

I also needed something like that recently. The only solution I found to get basename and dirname was using:

\Magento\Framework\Filesystem\Io\File

protected function someFunction()
{
    /** @var \Magento\Framework\Filesystem\Io\File $fileSystemIo **/
    $fileInfo = $this->fileSystemIo->getPathInfo('<absolutePath>');
    $basename = $fileInfo['basename']; 
    $dirname = $fileInfo['dirname'];
}
    

Before that, I tried using Magento\Framework\Filesystem\Directory\Write and getDriver() with no success. With them, you can get pretty much everything but not the basename.

3
  • YES. That's it. Thank you. I will award the bounty as soon as I am allowed to.
    – Marius
    Commented Nov 15, 2016 at 9:05
  • Marius are you really going to implement it that way? [\Magento\Framework\Filesystem\Io\File->getpathinfo][1] literally only calls [pathinfo][2] which in turn calls basename and dirname [1]: github.com/magento/magento2/blob/develop/lib/internal/Magento/… [2]: github.com/php/php-src/blob/master/ext/standard/string.c#L1662
    – Richard
    Commented Nov 15, 2016 at 9:48
  • 1
    @Richard. I saw that. For now I need/want to avoid certain functions. And in my specific case it fits nicely because I already had an instance of \Magento\Framework\Filesystem\Io\File injected in my own class for a different functionality. I just didn't know upfront about the getPathInfo method.
    – Marius
    Commented Nov 16, 2016 at 11:32
3

Luckily git lets us see when dirname and basename were forbidden, the reason is clearly, "Added files"

Looking at the issue for the ECG project you can see closed issues such as something bad in file_exists? #33, Error Functions #26, something bad in this functions? #17, Context / Explanation for the Rules #12, The use of function iconv() is forbidden #14 which would make me think that the initial list of forbidden functions wasn't given too much consideration, and magento are probably amenable to changing the forbidden list.

Searching the m2 codebase shows ~= 78 results for basename, a mix of variables and the code actually calling basename, including my favourite.

I think if I were you I'd post an issue on github and ask zlik if he still thinks they belong there or if M2 provides a wrapper

2

You can use object of SplFileInfo() class may be it will work.

$info = new SplFileInfo('/path/to/foo.txt');
var_dump($info->getFilename())

may be it will work.

you can also refer this url.

2
2

My suggestion would be to use the Magento/Backup module as an example.

The way the download action class is written would be interesting to look at because it also deals with real files to download:

public function execute()
{
    /* @var $backup \Magento\Backup\Model\Backup */
    $backup = $this->_backupModelFactory->create(
        $this->getRequest()->getParam('time'),
        $this->getRequest()->getParam('type')
    );

    if (!$backup->getTime() || !$backup->exists()) {
        /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
        $resultRedirect = $this->resultRedirectFactory->create();
        $resultRedirect->setPath('backup/*');
        return $resultRedirect;
    }

    $fileName = $this->_objectManager->get('Magento\Backup\Helper\Data')->generateBackupDownloadName($backup);

    $this->_fileFactory->create(
        $fileName,
        null,
        DirectoryList::VAR_DIR,
        'application/octet-stream',
        $backup->getSize()
    );

    /** @var \Magento\Framework\Controller\Result\Raw $resultRaw */
    $resultRaw = $this->resultRawFactory->create();
    $resultRaw->setContents($backup->output());
    return $resultRaw;
}

To me you should look at the way this method generates the file to download using \Magento\Framework\App\Response\Http\FileFactory and generateBackupDownloadName from Magento\Backup\Helper\Data (notice the recommended use of the OM ;) )

Another interesting bit

Another interesting thing you should look at is the getStorageData method from Magento\MediaStorage\Model\ResourceModel\File\Storage\File which itself calls directly dirname and basename but if you call that core method in your module, you won't get the forbidden errors ;)

public function getStorageData($dir = '/')
{
    $files = [];
    $directories = [];
    $directoryInstance = $this->_filesystem->getDirectoryRead(DirectoryList::MEDIA);
    if ($directoryInstance->isDirectory($dir)) {
        foreach ($directoryInstance->readRecursively($dir) as $path) {
            $itemName = basename($path);
            if ($itemName == '.svn' || $itemName == '.htaccess') {
                continue;
            }
            if ($directoryInstance->isDirectory($path)) {
                $directories[] = [
                    'name' => $itemName,
                    'path' => dirname($path) == '.' ? '/' : dirname($path),
                ];
            } else {
                $files[] = $path;
            }
        }
    }

    return ['files' => $files, 'directories' => $directories];
}

In a similar idea, there's also the collectFileInfo from Magento\MediaStorage\Helper\File\Media

6
  • generateBackupDownloadName uses some magic getters from the backup model. So they must have magic setters called before. I don't see anything related to basename or an alternative to it.
    – Marius
    Commented Nov 14, 2016 at 10:33
  • @Marius see my updated answer for another possible way Commented Nov 14, 2016 at 10:38
  • This could work. I will give it a try and come back with the results.
    – Marius
    Commented Nov 14, 2016 at 10:38
  • @Marius also check collectFileInfo from Magento\MediaStorage\Helper\File\Media ;) Commented Nov 14, 2016 at 10:41
  • collectFileInfo won't help me because it expects a file inside the media folder. Mine is in in the var folder. Also getStorageData has nothing to do with what I need. I don't want to collect all the files in a folder. I already have the file name.
    – Marius
    Commented Nov 14, 2016 at 14:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.