Magento – “Invalid Factory for nonexistent class” (setup:di:compile error)

code generationdifactorymagento-2.1magento2

I get the following error when running bin/magento setup:di:compile:

Errors during configuration scanning:
        IntegerNet\SolrSuggest\Implementor\Factory\AppFactory
                Invalid Factory for nonexistent class IntegerNet\SolrSuggest\Implementor\Factory\App in file [...]/vendor/integer-net/solr-magento2/src/Model/Cache.php

The AppFactory is actually a real interface from a library, it should not be generated by Magento. As far as I understood the generation, factories are only generated if the class does not exist. I double checked that the file exists and the directory is registered in vendor/composer/autoload_psr4.php.

This is how the class that depends on the factory looks like:

Cache.php

<?php
namespace IntegerNet\Solr\Model;
use IntegerNet\SolrSuggest\Implementor\Factory\AppFactory;
class Cache
{
    /**
     * @var AppFactory
     */
    private $appFactory;
    public function __construct(AppFactory $appFactory)
    {
        $this->appFactory = $appFactory;
    }
}

And this is the factory:

AppFactory.php

<?php
namespace IntegerNet\SolrSuggest\Implementor\Factory;
use IntegerNet\SolrSuggest\Implementor\Factory\CacheWriterFactory;
use IntegerNet\SolrSuggest\Implementor\Factory\ConfigFactory;
interface AppFactory extends ConfigFactory, CacheWriterFactory
{
}

I defined a preference for the interface in di.xml:

<preference for="IntegerNet\SolrSuggest\Implementor\Factory\AppFactory" type="IntegerNet\Solr\Model\Bridge\AppFactory" />

Any idea what I can do about these messages?

Observation

In developer mode where I don't explicitly run the compile command, I don't get any errors. And although I get these error messages, compilation runs through and everything works – which proves that the factory classes can be used.

Best Answer

That's an interesting issue and I'm not entirely sure why is this happening.

What I can suggest is debugging directly in the _findMissingClasses method from Magento/Setup/Module/Di/Code/Scanner/PhpScanner that's where the error is coming from:

protected function _findMissingClasses($file, $classReflection, $methodName, $entityType)
{
    $missingClasses = [];
    if ($classReflection->hasMethod($methodName)) {
        $constructor = $classReflection->getMethod($methodName);
        $parameters = $constructor->getParameters();
        /** @var $parameter \ReflectionParameter */
        foreach ($parameters as $parameter) {
            preg_match('/\[\s\<\w+?>\s([\w\\\\]+)/s', $parameter->__toString(), $matches);
            if (isset($matches[1]) && substr($matches[1], -strlen($entityType)) == $entityType) {
                $missingClassName = $matches[1];
                try {
                    if (class_exists($missingClassName)) {
                        continue;
                    }
                } catch (\RuntimeException $e) {
                }
                $sourceClassName = $this->getSourceClassName($missingClassName, $entityType);
                if (!class_exists($sourceClassName) && !interface_exists($sourceClassName)) {
                    $this->_log->add(
                        Log::CONFIGURATION_ERROR,
                        $missingClassName,
                        "Invalid {$entityType} for nonexistent class {$sourceClassName} in file {$file}"
                    );
                    continue;
                }
                $missingClasses[] = $missingClassName;
            }
        }
    }
    return $missingClasses;
}

That method is called from the _fetchFactories method which also logs the same error so you need to find out, where the error is coming from exactly and from there you should be able to start debugging:

protected function _fetchFactories($reflectionClass, $file)
{
    $factorySuffix = '\\'.ucfirst(FactoryGenerator::ENTITY_TYPE);
    $absentFactories = $this->_findMissingClasses(
        $file,
        $reflectionClass,
        '__construct',
        ucfirst(FactoryGenerator::ENTITY_TYPE)
    );
    foreach ($absentFactories as $key => $absentFactory) {
        if (substr($absentFactory, -strlen($factorySuffix)) == $factorySuffix) {
            $entityName = rtrim(substr($absentFactory, 0, -strlen($factorySuffix)), '\\');
            $this->_log->add(
                Log::CONFIGURATION_ERROR,
                $absentFactory,
                'Invalid Factory declaration for class ' . $entityName . ' in file ' . $file
            );
            unset($absentFactories[$key]);
        }
    }
    return $absentFactories;
}
Related Topic