Symlinked plugins no longer found after upgrade to PHP 8.1

Hi

I’m developing using October V3 on a Windows machine using Laragon.

I’ve installed my custom plugins using composer’s “path” repository method, which symlinks the plugin from a different location on my hard disk to my project’s “plugins” folder. Actually it “junctions” rather than symlinks but I’m no expert in the different linking types.

I just upgraded from PHP 8.0 to 8.1 and my plugins stopped working. They appear with a question mark next to them in the backend plugin manager.

I found this issue in PHP 8.1+, which seems to be the root cause - RecursiveDirectoryIterator regression wrt. junctions · Issue #9674 · php/php-src · GitHub

The code at \System\Classes\PluginManager::getVendorAndPluginNames() uses the same new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::FOLLOW_SYMLINKS)); method.

Can anything be done about this (assuming this is the problem) or do I need to sacrifice something in my dev setup?

Thanks.

If it helps, this seems to fix it - although be aware this was very quick and rough and not intended as final code …

\System\Classes\PluginManager

    public function getVendorAndPluginNames()
    {
        $plugins = [];

        $dirPath = plugins_path();
        if (!is_dir($dirPath)) {
            return $plugins;
        }

        $iterator = new \DirectoryIterator($dirPath);

        foreach ($iterator as $entry) {
            if (!$entry->isDir() || $entry->isDot()) {
                continue;
            }

            $vendorName = $entry->getFilename();

            if ($vendorName === "." || $vendorName === "..") {
                continue;
            }

            $vendorPath = $dirPath . DIRECTORY_SEPARATOR . $vendorName;

            $vendorIterator = new \DirectoryIterator($vendorPath);

            foreach ($vendorIterator as $pluginEntry) {
                if (!$pluginEntry->isDir() || $pluginEntry->isDot()) {
                    continue;
                }

                $pluginName = $pluginEntry->getFilename();

                if ($pluginName === "." || $pluginName === "..") {
                    continue;
                }

                $pluginFilePath = $vendorPath . DIRECTORY_SEPARATOR . $pluginName . DIRECTORY_SEPARATOR . "plugin.php";
                if (file_exists($pluginFilePath)) {
                    $plugins[$vendorName][$pluginName] = "$vendorName/$pluginName";
                }
            }
        }

        return $plugins;
    }
1 Like

Hopefully, they will fix this at the PHP level soon. The current implementation uses RecursiveDirectoryIterator to locate plugins in a single call.

Is there any way a different directory scanning method could be used for PHP 8.1+?
If speed is an issue, perhaps enable/disable the method used via a config setting or something? Or automatically set a flag if using repo paths in composer.json?

I know I’m clutching at straws and understand it’s probably too much faff for such an edge case but the easiest solution for me at the moment is to hack the PluginManager code every time I update locally, which isn’t ideal but not the worst thing ever. Anything else would be a big change to my dev setup.

I’m honestly a little surprised more people don’t run into this. Ah well, like you say, hopefully they will fix this soon at the PHP level.

Thanks for your time.

If we find PHP team are not willing to fix it, then October should implement a permanent solution.