alekitto / class-finder

Utility classes to help discover other classes/namespaces
https://alekitto.github.io/class-finder/
MIT License
27 stars 5 forks source link

Fatal error when using `ComposerFinder` with the Symfony Cache Component #13

Closed llaville closed 5 months ago

llaville commented 5 months ago

Hello @alekitto

I've just discover your package with recent major release 7.0 of GraphQLite

I've tried it on my new package still under development (and not yet published) that use the Symfony Cache Component, and got finding class issue.

Here is how to reproduce :

A simple composer.json with such contents

{
    "require": {
        "symfony/cache": "^6.4 || ^7.0",
        "kcs/class-finder": "^0.4.0"
    }
}

The script to reproduce error

<?php

require_once 'vendor/autoload.php';

use Kcs\ClassFinder\Finder\ComposerFinder;

$finder = new ComposerFinder();
//$finder->notPath('vendor/symfony/cache');

$count = 0;
foreach ($finder as $className => $reflector) {
    printf('- %s '. PHP_EOL, $className);
    ++$count;
}
printf('> found %d class(es)' . PHP_EOL, $count);

There is a strange entry - � in class list on results above

find results ```text - Composer\InstalledVersions - Safe\DateTime - Safe\DateTimeImmutable - Safe\Exceptions\ApacheException - Safe\Exceptions\ApcException - Safe\Exceptions\ApcuException - Safe\Exceptions\ArrayException - Safe\Exceptions\Bzip2Exception - Safe\Exceptions\CalendarException - Safe\Exceptions\ClassobjException - Safe\Exceptions\ComException - Safe\Exceptions\CubridException - Safe\Exceptions\CurlException - Safe\Exceptions\DatetimeException - Safe\Exceptions\DirException - Safe\Exceptions\EioException - Safe\Exceptions\ErrorfuncException - Safe\Exceptions\ExecException - Safe\Exceptions\FileinfoException - Safe\Exceptions\FilesystemException - Safe\Exceptions\FilterException - Safe\Exceptions\FpmException - Safe\Exceptions\FtpException - Safe\Exceptions\FunchandException - Safe\Exceptions\GettextException - Safe\Exceptions\GmpException - Safe\Exceptions\GnupgException - Safe\Exceptions\HashException - Safe\Exceptions\IbaseException - Safe\Exceptions\IbmDb2Exception - Safe\Exceptions\IconvException - Safe\Exceptions\ImageException - Safe\Exceptions\ImapException - Safe\Exceptions\InfoException - Safe\Exceptions\InotifyException - Safe\Exceptions\JsonException - Safe\Exceptions\LdapException - Safe\Exceptions\LibeventException - Safe\Exceptions\LibxmlException - Safe\Exceptions\LzfException - Safe\Exceptions\MailparseException - Safe\Exceptions\MbstringException - Safe\Exceptions\MiscException - Safe\Exceptions\MssqlException - Safe\Exceptions\MysqlException - Safe\Exceptions\MysqliException - Safe\Exceptions\NetworkException - Safe\Exceptions\Oci8Exception - Safe\Exceptions\OpcacheException - Safe\Exceptions\OpensslException - Safe\Exceptions\OutcontrolException - Safe\Exceptions\PasswordException - Safe\Exceptions\PcntlException - Safe\Exceptions\PcreException - Safe\Exceptions\PgsqlException - Safe\Exceptions\PosixException - Safe\Exceptions\PsException - Safe\Exceptions\PspellException - Safe\Exceptions\ReadlineException - Safe\Exceptions\RpminfoException - Safe\Exceptions\RrdException - Safe\Exceptions\SafeExceptionInterface - Safe\Exceptions\SemException - Safe\Exceptions\SessionException - Safe\Exceptions\ShmopException - Safe\Exceptions\SimplexmlException - Safe\Exceptions\SocketsException - Safe\Exceptions\SodiumException - Safe\Exceptions\SolrException - Safe\Exceptions\SplException - Safe\Exceptions\SqlsrvException - Safe\Exceptions\SsdeepException - Safe\Exceptions\Ssh2Exception - Safe\Exceptions\StatsException - Safe\Exceptions\StreamException - Safe\Exceptions\StringsException - Safe\Exceptions\SwooleException - Safe\Exceptions\UodbcException - Safe\Exceptions\UopzException - Safe\Exceptions\UrlException - Safe\Exceptions\VarException - Safe\Exceptions\XdiffException - Safe\Exceptions\XmlException - Safe\Exceptions\XmlrpcException - Safe\Exceptions\YamlException - Safe\Exceptions\YazException - Safe\Exceptions\ZipException - Safe\Exceptions\ZlibException - � - Symfony\Contracts\Service\Attribute\SubscribedService - Symfony\Contracts\Service\Attribute\Required - Symfony\Contracts\Service\ResetInterface - Symfony\Contracts\Service\ServiceLocatorTrait - Symfony\Contracts\Service\ServiceProviderInterface - Symfony\Contracts\Service\ServiceSubscriberInterface - Symfony\Contracts\Service\ServiceSubscriberTrait - Symfony\Contracts\Cache\CacheInterface - Symfony\Contracts\Cache\CacheTrait - Symfony\Contracts\Cache\CallbackInterface - Symfony\Contracts\Cache\ItemInterface - Symfony\Contracts\Cache\TagAwareCacheInterface - Symfony\Component\VarExporter\Exception\NotInstantiableTypeException - Symfony\Component\VarExporter\Exception\LogicException - Symfony\Component\VarExporter\Exception\ClassNotFoundException - Symfony\Component\VarExporter\Exception\ExceptionInterface - Symfony\Component\VarExporter\Hydrator - Symfony\Component\VarExporter\Instantiator - Symfony\Component\VarExporter\Internal\LazyObjectTrait - Symfony\Component\VarExporter\Internal\LazyObjectRegistry - Symfony\Component\VarExporter\Internal\LazyObjectState - Symfony\Component\VarExporter\Internal\Reference - Symfony\Component\VarExporter\Internal\Hydrator - Symfony\Component\VarExporter\Internal\Registry - Symfony\Component\VarExporter\Internal\Values - Symfony\Component\VarExporter\Internal\Exporter - Symfony\Component\VarExporter\LazyGhostTrait - Symfony\Component\VarExporter\LazyObjectInterface - Symfony\Component\VarExporter\LazyProxyTrait - Symfony\Component\VarExporter\ProxyHelper - Symfony\Component\VarExporter\VarExporter - Symfony\Component\Cache\Adapter\NullAdapter - Symfony\Component\Cache\Adapter\ProxyAdapter - Symfony\Component\Cache\Adapter\DoctrineDbalAdapter - Symfony\Component\Cache\Adapter\AbstractAdapter - Symfony\Component\Cache\Adapter\TagAwareAdapterInterface - Symfony\Component\Cache\Adapter\MemcachedAdapter - Symfony\Component\Cache\Adapter\ArrayAdapter - Symfony\Component\Cache\Adapter\Psr16Adapter - Symfony\Component\Cache\Adapter\AdapterInterface - Symfony\Component\Cache\Adapter\ParameterNormalizer - Symfony\Component\Cache\Adapter\PhpFilesAdapter - Symfony\Component\Cache\Adapter\FilesystemAdapter - Symfony\Component\Cache\Adapter\TagAwareAdapter - Symfony\Component\Cache\Adapter\RedisTagAwareAdapter - Symfony\Component\Cache\Adapter\AbstractTagAwareAdapter - Symfony\Component\Cache\Adapter\ChainAdapter - Symfony\Component\Cache\Adapter\FilesystemTagAwareAdapter - Symfony\Component\Cache\Adapter\PdoAdapter - Symfony\Component\Cache\Adapter\ApcuAdapter - Symfony\Component\Cache\Adapter\CouchbaseBucketAdapter - Symfony\Component\Cache\Adapter\TraceableAdapter - Symfony\Component\Cache\Adapter\PhpArrayAdapter - Symfony\Component\Cache\Adapter\RedisAdapter - Symfony\Component\Cache\Adapter\TraceableTagAwareAdapter - Symfony\Component\Cache\Adapter\CouchbaseCollectionAdapter - Symfony\Component\Cache\CacheItem - Symfony\Component\Cache\Exception\LogicException - Symfony\Component\Cache\Exception\InvalidArgumentException - Symfony\Component\Cache\Exception\CacheException - Symfony\Component\Cache\LockRegistry - Symfony\Component\Cache\Marshaller\TagAwareMarshaller - Symfony\Component\Cache\Marshaller\DefaultMarshaller - Symfony\Component\Cache\Marshaller\SodiumMarshaller - Symfony\Component\Cache\Marshaller\MarshallerInterface - Symfony\Component\Cache\Marshaller\DeflateMarshaller - Symfony\Component\Cache\Messenger\EarlyExpirationMessage - Symfony\Component\Cache\Messenger\EarlyExpirationDispatcher - Symfony\Component\Cache\Messenger\EarlyExpirationHandler - Symfony\Component\Cache\PruneableInterface - Symfony\Component\Cache\ResettableInterface - Symfony\Component\Cache\Traits\ContractsTrait - Symfony\Component\Cache\Traits\RedisClusterProxy - Symfony\Component\Cache\Traits\FilesystemTrait - Symfony\Component\Cache\Traits\Redis6Proxy Fatal error: Declaration of Symfony\Component\Cache\Traits\Redis5Proxy::_compress($value) must be compatible with Redis::_compress(string $value): string in /shared/backups/github/class-finder-issues/vendor/symfony/cache/Traits/Redis5Proxy.php on line 64 Call Stack: 0.0002 697600 1. {main}() /shared/backups/github/class-finder-issues/find.php:0 0.0661 8288544 2. Kcs\ClassFinder\Iterator\ClassIterator->next() /shared/backups/github/class-finder-issues/find.php:16 0.0661 8288544 3. Generator->next() /shared/backups/github/class-finder-issues/vendor/kcs/class-finder/lib/Iterator/ClassIterator.php:60 0.0661 8288544 4. Kcs\ClassFinder\Iterator\ClassIterator->next() /shared/backups/github/class-finder-issues/vendor/kcs/class-finder/lib/Iterator/ComposerIterator.php:81 0.0661 8288544 5. Generator->next() /shared/backups/github/class-finder-issues/vendor/kcs/class-finder/lib/Iterator/ClassIterator.php:60 0.0661 8288544 6. Kcs\ClassFinder\Iterator\Psr4Iterator->getGenerator() /shared/backups/github/class-finder-issues/vendor/kcs/class-finder/lib/Iterator/ClassIterator.php:60 0.0661 8288624 7. Kcs\ClassFinder\Iterator\{closure:/shared/backups/github/class-finder-issues/vendor/kcs/class-finder/lib/Iterator/Psr4Iterator.php:59-61}($path = '/shared/backups/github/class-finder-issues/vendor/symfony/cache/Traits/Redis5Proxy.php') /shared/backups/github/class-finder-issues/vendor/kcs/class-finder/lib/Iterator/Psr4Iterator.php:96 0.0671 8630960 8. include_once('/shared/backups/github/class-finder-issues/vendor/symfony/cache/Traits/Redis5Proxy.php') /shared/backups/github/class-finder-issues/vendor/kcs/class-finder/lib/Iterator/Psr4Iterator.php:60 ```

NOTE no workaround seems possible (even with notPath)

alekitto commented 5 months ago

Hi @llaville, Thanks for opening the issue and the reproducer, I'll take it as soon as possible.

In the meantime, I have to ask you to try the same code but with authoritative classmap (you have to run composer install -o to generate one) and check if the result is correct or is exactly the same you posted.

llaville commented 5 months ago

@alekitto

In the meantime, I have to ask you to try the same code but with authoritative classmap (you have to run composer install -o to generate one) and check if the result is correct or is exactly the same you posted.

Same results :( sorry

alekitto commented 5 months ago

Ok, I've investigated this: unfortunately, I've discovered that Symfony\Component\Cache\Traits namespace is pretty problematic

  1. Symfony is using class_alias to support both phpredis extension v5 and v6 under RedisProxy class in RedisProxy.php. This works when you require RedisProxy, but raise a fatal error when one of the underlying classes is requested AND the extension is loaded at the wrong version
  2. ValueWrapper.php has a non-namespaced non-alphanumeric class which is represented by in your output. It is included in the classmap, so it is always loaded.

There are two workarounds to avoid the fatal error you can exclude the namespace entirely like:

$finder = new ComposerFinder();
$finder->notInNamespace('Symfony\Component\Cache\Traits');

or passing a path callback and excluding the problematic files, like this:

$finder = new ComposerFinder();
$finder->pathFilter(static fn (string $path): bool =>
    !preg_match('#symfony/cache/Traits/Redis(?:Cluster)?\dProxy\.php$#', $path));

In this case $path is normalized and this should work on Windows too. These have been tried on the dev-master version, so I'm not 100% sure they will work on the released version too.

Anyways, I'll probably start to compile a list of "problematic" namespaces/files to exclude them by default in some way.

llaville commented 5 months ago

I really like your research of origin of this issue and your explains. It's very clear (I appreciate a lot) !

I confirm that both of your solutions works fine with current release 0.4.0

But I prefer the second one with pathFilter that allows to find more classes (221) than the first one (212)

Here are extra files found on second solution compared to first one

- Symfony\Component\Cache\Traits\AbstractAdapterTrait
- Symfony\Component\Cache\Traits\ContractsTrait
- Symfony\Component\Cache\Traits\FilesystemCommonTrait
- Symfony\Component\Cache\Traits\FilesystemTrait
- Symfony\Component\Cache\Traits\ProxyTrait
- Symfony\Component\Cache\Traits\RedisClusterNodeProxy
- Symfony\Component\Cache\Traits\RedisClusterProxy
- Symfony\Component\Cache\Traits\RedisProxy
- Symfony\Component\Cache\Traits\RedisTrait
alekitto commented 5 months ago

I've implemented the skipBogonFiles method to call on finders to exclude problematic files. ATM it ignores only the files listed here, but more can be added in the future.