Skip to content

Feature: iterable source file path names for ColocatedMappingDriver #429

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

rela589n
Copy link

This PR follows from #424.

It aims to rework ColocatedMappingDriver so that it won't have the responsibility of scanning for files in the provided directories, but will instead accept an iterable of source file path names. Thus, every individual component using ColocatedMappingDriver should provide a complete iterator that covers all files.

Ultimately, Symfony Finder should be used to grab a list of PHP files that must be mapped. This aims for more flexibility regarding file scanning, so that users would be able to customize what files to include.

@greg0ire
Copy link
Member

greg0ire commented Aug 1, 2025

Why are there 9 commits? Please make fewer commits, with better messages.
See the contributing guide.

rela589n added a commit to rela589n/doctrine-persistence that referenced this pull request Aug 2, 2025
This is necessary, since in doctrine#429 we would like to use a different way of "finding files" - using a client-defined iterable. Thus, the only way to do the switch is to split the loops.
@rela589n rela589n force-pushed the feat-collocated-mapping-driver-iterable-files branch 2 times, most recently from db80dd4 to 92a392d Compare August 2, 2025 09:04
`i` flag is removed - it makes search case-insensitive. There's no point in making search case-insensitive for an extension regex (`.php`), since the extension is always lowercase.

`GET_MATCH` is changed into `MATCH` - using `MATCH` is more obvious, since there are **NO implicit modifications** of the result as in the case of `GET_MATCH`, which returns only the "matched" part of the path string instead of the actual `SplFileInfo` item.

`^.+` part of regex is removed - it's no longer necessary, since it had only been required for `GET_MATCH` to return the full path. Now, with `MATCH`, regex only affects results filtering and does not affect the results themselves.
This is necessary, since in doctrine#429 we would like to use a different way of "finding files" - using a client-defined iterable. Thus, the only way to do the switch is to split the loops.
…e path names

Before it was only possible to pass an array of paths where to look for. Now, it's possible to explicitly provide fine-grained iterable of only necessary files to the Driver. It's anticipated to use Symfony Finder in the Bundles using this library. This gives much flexibility for client code to configure which files should be included and which should not.
@rela589n rela589n force-pushed the feat-collocated-mapping-driver-iterable-files branch from 92a392d to a6cb5a1 Compare August 4, 2025 10:20
@greg0ire greg0ire added this to the 4.1.0 milestone Aug 5, 2025
@greg0ire greg0ire merged commit beef6b3 into doctrine:4.1.x Aug 5, 2025
7 checks passed
@greg0ire
Copy link
Member

greg0ire commented Aug 5, 2025

Thanks @rela589n !

@rela589n
Copy link
Author

rela589n commented Aug 5, 2025

Thank you, @greg0ire

So, the depending libraries need to be updated now.
I'm sure that doctrine/orm is the first to be updated to allow passing an iterable into AttributeDriver, so I plan to do this next.

Do you have any considerations about how to do this properly?

@greg0ire
Copy link
Member

greg0ire commented Aug 5, 2025

Do something similar to what you've done in your test, then contribute to DoctrineBundle a service (could be based on a symfony/finder) that does exactly what the legacy code does. It should also possible to provide another service instead, so that you can finally reach your personal goal.

I guess the same needs to be done on the ODM part.

Then you can add deprecations.

@rela589n
Copy link
Author

rela589n commented Aug 5, 2025

Do something similar to what you've done in your test

Sure! The only question I have right now is how to make things mesh together, since the changes made to doctrine/persistence haven't been released yet, and the changes to doctrine/orm would require them. I could temporarily "require-dev": { "doctrine/persistence": "4.1.x-dev"} to make it work, but what is the correct way of doing this? If require-dev is acceptable, I can proceed with this.

that does exactly what the legacy code does

So, you mean the service that can accept an array of paths, and will implement iterable<string> that is needed for AttributeDriver?

It should also possible to provide another service instead, so that you can finally reach your personal goal.

If I understand correctly, it could be achieved via a Compiler Pass

@greg0ire
Copy link
Member

greg0ire commented Aug 5, 2025

Sure! The only question I have right now is how to make things mesh together, since the changes made to doctrine/persistence haven't been released yet, and the changes to doctrine/orm would require them. I could temporarily "require-dev": { "doctrine/persistence": "4.1.x-dev"} to make it work, but what is the correct way of doing this? If require-dev is acceptable, I can proceed with this.

You can start like this, then before merging we release 4.1.0. That way, if a breaking change needs to be made, we can still do it before releasing.

So, you mean the service that can accept an array of paths, and will implement iterable that is needed for AttributeDriver?

Right now, there is a way to configure the paths through config, right? Well that service should be able to reuse that config to do the same thing using the new way.

rela589n added a commit to rela589n/doctrine-persistence that referenced this pull request Aug 5, 2025
…d paths when `ColocatedMappingDriver::addPaths()` is called after instance creation.

After github.com/doctrine/pull/429/ had been merged, it could bring in subtle bugs during migration toward the new `$sourceFilePathNames` approach. If the client code had called `addPaths()` after the instance creation, it would've been ignored (without any warning or so), and paths would not have been used. This commit fixes that. Retroactively added paths will be taken into account as well as ``$sourceFilePathNames`.
rela589n added a commit to rela589n/doctrine-persistence that referenced this pull request Aug 5, 2025
…d paths when `ColocatedMappingDriver::addPaths()` is called after instance creation.

After github.com/doctrine/pull/429/ had been merged, it could bring in subtle bugs during migration toward the new `$sourceFilePathNames` approach. If the client code had called `addPaths()` after the instance creation, it would've been ignored (without any warning or so), and paths would not have been used. This commit fixes that. Retroactively added paths will be taken into account as well as ``$sourceFilePathNames`.
rela589n added a commit to rela589n/doctrine-persistence that referenced this pull request Aug 5, 2025
…d paths when `ColocatedMappingDriver::addPaths()` is called after instance creation.

After github.com/doctrine/pull/429/ had been merged, it could bring in subtle bugs during migration toward the new `$sourceFilePathNames` approach. If the client code had called `addPaths()` after the instance creation, it would've been ignored (without any warning or so), and paths would not have been used. This commit fixes that. Retroactively added paths will be taken into account as well as ``$sourceFilePathNames`.
rela589n added a commit to rela589n/doctrine-persistence that referenced this pull request Aug 6, 2025
…d paths.

After github.com/doctrine/pull/429/ had been merged,
it could bring in subtle bugs during migration toward the new
`$sourceFilePathNames` approach. If the client code had called
`addPaths()` after the instance creation, it would've been ignored
(without any warning or so), and paths would not have been used.
This commit fixes that. Retroactively added paths will be taken
into account as well as ``$sourceFilePathNames`.
rela589n added a commit to rela589n/doctrine-persistence that referenced this pull request Aug 6, 2025
After github.com/doctrine/pull/429/ was merged,
it could introduce subtle bugs during migration toward the new
`$sourceFilePathNames` approach. If client code called
`addPaths()` after instance creation, it would be ignored
(without any warning), and paths would not be used. The current
commit fixes that. Retroactively added paths will be taken
into account along with the `$sourceFilePathNames` iterable.
rela589n added a commit to rela589n/doctrine-orm that referenced this pull request Aug 6, 2025
In the scope of doctrine/persistence#429
there was added `ColocatedMappingDriver::$sourceFilePathNames`,
which is available starting from `doctrine/persistence` >= 4.1.
This commit integrates those changes into `AttributeDriver`.
Since `doctrine/orm` maintains the support for
`doctrine/persistence` of older versions, `AttributeDriver` ensures
that `$sourceFilePathNames` is actually defined. Tests use
`InstalledVersions` to opt into new behavior if `doctrine/persistence`
is at least version 4.1. New behavior is adapted by using `glob()` on paths.
rela589n added a commit to rela589n/doctrine-orm that referenced this pull request Aug 7, 2025
In the scope of doctrine/persistence#429
there was added `ColocatedMappingDriver::$sourceFilePathNames`,
which is available starting from `doctrine/persistence` >= 4.1.
This commit integrates those changes into `AttributeDriver`.
Since `doctrine/orm` maintains the support for
`doctrine/persistence` of older versions, `AttributeDriver` ensures
that `$sourceFilePathNames` is actually defined. Tests use
`InstalledVersions` to opt into new behavior if `doctrine/persistence`
is at least version 4.1. New behavior is adapted by using `glob()` on paths.
rela589n added a commit to rela589n/doctrine-orm that referenced this pull request Aug 7, 2025
In the scope of doctrine/persistence#429
(available from `doctrine/persistence` >= 4.1) there was added
`ColocatedMappingDriver::$sourceFilePathNames`, which allows
passing the iterable of file paths for the mapping driver to use.
This commit integrates those changes into `AttributeDriver`.
Since `doctrine/orm` maintains the support for `doctrine/persistence`
of older versions, `AttributeDriver` ensures that `$sourceFilePathNames`
is actually defined. Tests use `InstalledVersions` to opt into new
behaviour if `doctrine/persistence` is at least version 4.1.
The new behaviour is adapted by using `glob()` on paths.
rela589n added a commit to rela589n/doctrine-orm that referenced this pull request Aug 7, 2025
In the scope of doctrine/persistence#429
(available from `doctrine/persistence` >= 4.1) there was added
`ColocatedMappingDriver::$sourceFilePathNames`, which allows
passing the iterable of file paths for the mapping driver to use.
This commit integrates those changes into `AttributeDriver`.
Since `doctrine/orm` maintains the support for `doctrine/persistence`
of older versions, `AttributeDriver` ensures that `$sourceFilePathNames`
is actually defined. Tests use `InstalledVersions` to opt into new
behaviour if `doctrine/persistence` is at least version 4.1.
`AttributeDriver` now accepts a new boolean to opt into the new
behaviour. This flag is used to distinguish between an array of
directory paths (the existing way) and iterable of file paths
(the new way). The old behaviour can be adapted into new by using
`glob()` search on directory paths.
rela589n added a commit to rela589n/doctrine-orm that referenced this pull request Aug 7, 2025
In the scope of doctrine/persistence#429
(available from `doctrine/persistence` >= 4.1) there was added
`ColocatedMappingDriver::$sourceFilePathNames`, which allows
passing the iterable of file paths for the mapping driver to use.
This commit integrates those changes into `AttributeDriver`.
Since `doctrine/orm` maintains the support for `doctrine/persistence`
of older versions, `AttributeDriver` ensures that `$sourceFilePathNames`
is actually defined. Tests use `InstalledVersions` to opt into new
behaviour if `doctrine/persistence` is at least version 4.1.
`AttributeDriver` now accepts a new boolean to opt into the new
behaviour. This flag is used to distinguish between an array of
directory paths (the existing way) and iterable of file paths
(the new way). The old behaviour can be adapted into new by using
`glob()` search on directory paths.
rela589n added a commit to rela589n/doctrine-mongodb-odm that referenced this pull request Aug 7, 2025
In the scope of doctrine/persistence#429
(available from `doctrine/persistence` >= 4.1) there was added
`ColocatedMappingDriver::$sourceFilePathNames`, which allows
passing an iterable of file paths for the mapping driver to use.
This commit integrates those changes into `AttributeDriver`
(and `AnnotationDriver`).

Since `doctrine/orm` maintains the support
for `doctrine/persistence` of older versions, the `AttributeDriver`
ensures that `$sourceFilePathNames` is actually defined.
Tests use `InstalledVersions` to opt into new behaviour if
`doctrine/persistence` is at least version 4.1.

`AttributeDriver` now accepts a new boolean to opt into the new
behaviour. This flag is used to distinguish between an array of
directory paths (the existing way) and the iterable of file paths
(the new way). The old behaviour can be adapted into new by using
`glob()` search on directory paths.
rela589n added a commit to rela589n/doctrine-mongodb-odm that referenced this pull request Aug 7, 2025
In the scope of doctrine/persistence#429
(available from `doctrine/persistence` >= 4.1) there was added
`ColocatedMappingDriver::$sourceFilePathNames`, which allows
passing an iterable of file paths for the mapping driver to use.
This commit integrates those changes into `AttributeDriver`
(and `AnnotationDriver`).

Since `doctrine/orm` maintains the support
for `doctrine/persistence` of older versions, the `AttributeDriver`
ensures that `$sourceFilePathNames` is actually defined.
Tests use `InstalledVersions` to opt into new behaviour if
`doctrine/persistence` is at least version 4.1.

`AttributeDriver` now accepts a new boolean to opt into the new
behaviour. This flag is used to distinguish between an array of
directory paths (the existing way) and the iterable of file paths
(the new way). The old behaviour can be adapted into the new one by using `glob()` search on directory paths.
rela589n added a commit to rela589n/doctrine-mongodb-odm that referenced this pull request Aug 7, 2025
In the scope of doctrine/persistence#429
(available from `doctrine/persistence` >= 4.1) there was added
`ColocatedMappingDriver::$sourceFilePathNames`, which allows
passing an iterable of file paths for the mapping driver to use.
This commit integrates those changes into `AttributeDriver`
(and `AnnotationDriver`).

Since `doctrine/orm` maintains the support
for `doctrine/persistence` of older versions, the `AttributeDriver`
ensures that `$sourceFilePathNames` is actually defined.
Tests use `InstalledVersions` to opt into new behaviour if
`doctrine/persistence` is at least version 4.1.

`AttributeDriver` now accepts a new boolean to opt into the new
behaviour. This flag is used to distinguish between an array of
directory paths (the existing way) and the iterable of file paths
(the new way). The old behaviour can be adapted into the new one by using `glob()` search on directory paths.
rela589n added a commit to rela589n/doctrine-mongodb-odm that referenced this pull request Aug 7, 2025
In the scope of doctrine/persistence#429
(available from `doctrine/persistence` >= 4.1) there was added
`ColocatedMappingDriver::$sourceFilePathNames`, which allows
passing an iterable of file paths for the mapping driver to use.
This commit integrates those changes into `AttributeDriver`
(and `AnnotationDriver`).

Since `doctrine/orm` maintains the support
for `doctrine/persistence` of older versions, the `AttributeDriver`
ensures that `$sourceFilePathNames` is actually defined.
Tests use `InstalledVersions` to opt into new behaviour if
`doctrine/persistence` is at least version 4.1.

`AttributeDriver` now accepts a new boolean to opt into the new
behaviour. This flag is used to distinguish between an array of
directory paths (the existing way) and the iterable of file paths
(the new way). The old behaviour can be adapted into the new one by using `glob()` search on directory paths.
rela589n added a commit to rela589n/doctrine-mongodb-odm that referenced this pull request Aug 7, 2025
In the scope of doctrine/persistence#429
(available from `doctrine/persistence` >= 4.1) there was added
`ColocatedMappingDriver::$sourceFilePathNames`, which allows
passing an iterable of file paths for the mapping driver to use.
This commit integrates those changes into `AttributeDriver`
(and `AnnotationDriver`).

Since `doctrine/orm` maintains the support
for `doctrine/persistence` of older versions, the `AttributeDriver`
ensures that `$sourceFilePathNames` is actually defined.
Tests use `InstalledVersions` to opt into new behaviour if
`doctrine/persistence` is at least version 4.1.

`AttributeDriver` now accepts a new boolean to opt into the new
behaviour. This flag is used to distinguish between an array of
directory paths (the existing way) and the iterable of file paths
(the new way). The old behaviour can be adapted into the new one by using `glob()` search on directory paths.
rela589n added a commit to rela589n/doctrine-orm that referenced this pull request Aug 7, 2025
In the scope of doctrine/persistence#429
(available from `doctrine/persistence` >= 4.1) there was added
`ColocatedMappingDriver::$sourceFilePathNames`, which allows
passing the iterable of file paths for the mapping driver to use.
This commit integrates those changes into `AttributeDriver`.
Since `doctrine/orm` maintains the support for `doctrine/persistence`
of older versions, `AttributeDriver` ensures that `$sourceFilePathNames`
is actually defined. Tests use `InstalledVersions` to opt into new
behaviour if `doctrine/persistence` is at least version 4.1.
`AttributeDriver` now accepts a new boolean to opt into the new
behaviour. This flag is used to distinguish between an array of
directory paths (the existing way) and iterable of file paths
(the new way). The old behaviour can be adapted into new by using
`glob()` search on directory paths.
@greg0ire
Copy link
Member

greg0ire commented Aug 7, 2025

From @GromNaN in doctrine/mongodb-odm#2794 (comment):

I was too quick in my first review. I agree it's simpler to have either a list of dir names or a list of file names. But then I would remove the $pathsAsFilePaths parameter and detect it instead.

$pathsAsFilePaths = match(true) {
    is_file($paths[0]) => true,
    is_dir($paths[0]) => false,
    default => throw new RuntimeException
}

And then loop and validate that we only have dirs or file names. I'm not sure if missing dirs or files are skipped or throw an error, but that can be handled.

The iterable doesn't necessarily implement ArrayAccess, but maybe it's still possible to do something like this:

$pathsAsFilePaths = false;
$first = true;
foreach ($paths as $path) {
    if ($first && is_file($path)) {
        $pathsAsFilePaths = true;
    }
}

A bit weird, but if it allows not having to change the method signature, it sounds great!

@greg0ire
Copy link
Member

greg0ire commented Aug 7, 2025

Extra question: do you think there might be cases where people would need to call addPaths() with an iterable of files, then call it again with an array of directories? I'm thinking that might be the case if there is some mapping configuration provided by a bundle like FOSUserBundle

The answer will impact #430

I'm starting to think the best implementation could be to do something like this

foreach ($paths as $path) {
    switch(true) {
        case is_file($path):
            $this->newLogic($path);
            break;

        case is_dir($path):
            $this->legacyLogic($path);
            break;

        default:
            throw new \InvalidArgumentException(sprintf(
                '"%s" is neither a file nor a directory.',
                $path,
            ));
    }
}

legacyLogic() could call newLogic() with what it found inside the directory (don't actually name the methods like this please) .

A side effect of this is that it would allow iterable<dir|file> when before we only allowed iterable<dir>|iterable<file>. I'm not saying it's useful, but it would certainly be a side effect.

@rela589n
Copy link
Author

rela589n commented Aug 8, 2025

Don't you think that this will complicate the interface? iterable<dir|file> is harder to deal with, harder to deprecate, harder to migrate. People who'd pass iterable<dir|file> would need to rework it toward iterable<file>, which is not that straightforward.

If it be so, why would we not allow either array<dir> (as currently) or iterable<file>? It's possible to check the type of iterable - whether it's a file or a directory, similar to the proposed:

$pathsAsFilePaths = match(true) {
    is_file($paths[0]) => true,
    is_dir($paths[0]) => false,
    default => throw new RuntimeException();
}

With the difference that it doesn't require ArrayAccess:

$pathsAsFilePaths = true;

foreach ($paths as $path) {
    if (!is_file($path)) {
        $pathsAsFilePaths = false;
    }

    break;
}

Since we plan to migrate toward the iterable of files, IMO we should not allow iterable<dir|file>, lest people start using it this way.

@greg0ire
Copy link
Member

greg0ire commented Aug 8, 2025

Makes sense. So while it would be technically possible with the code I mentioned, let's not document that it's possible. Do you have thoughts about my FOSUserBundle question?

@rela589n
Copy link
Author

rela589n commented Aug 8, 2025

Extra question: do you think there might be cases where people would need to call addPaths() with an iterable of files, then call it again with an array of directories? I'm thinking that might be the case if there is some mapping configuration provided by a bundle like FOSUserBundle

Can't see any addPaths() there https://github.com/search?q=repo%3AFriendsOfSymfony%2FFOSUserBundle+%22addPaths%22&type=code

Anyway, if the driver uses an iterable of files, there's no need for files to be added via addPaths(). If they need to do so, they'd be able to pass the mutable iterable. When using Symfony Finder, one could modify the Finder after it has been created.

One more question I have, though, is whether https://github.com/doctrine/DoctrineBundle is going to be updated so that iterable is the new default? Do we check for property_exists(AttributeDriver::class, 'sourceFilePathNames'), like it was in AttributeDriver, or check for the installed version instead?

@greg0ire
Copy link
Member

greg0ire commented Aug 8, 2025

Yes, I think iterable should be the new default, and that we should use property_exists for consistency's sake.

@rela589n
Copy link
Author

rela589n commented Aug 8, 2025

should use property_exists

Understand. I asked because it's private, and I was hesitant to check if the property exists outside of the class.

So, what's next? Should I go and implement the driver without any extra arguments?

@greg0ire
Copy link
Member

greg0ire commented Aug 8, 2025

So, what's next? Should I go and implement the driver without any extra arguments?

Yes. If you want me to, I can revert #429 (or maybe only a6cb5a1), then you can submit something with a revert of the revert, your modifications, and modifications from #430, it might be easier for everyone to review.

@rela589n
Copy link
Author

rela589n commented Aug 8, 2025

Let's do it

@greg0ire
Copy link
Member

greg0ire commented Aug 8, 2025

#431

@GromNaN
Copy link
Member

GromNaN commented Aug 8, 2025

I think iterable should be the new default.

Yes!

When setting the $this->paths array, an iterators should be created internally for backward compatibility.

The constructor will accept string[]|Iterator<string>.

  • string[] is a list of dir names, it should trigger a deprecation when this is used.
  • Iterator<string> is a list of file names.

The methods addPaths, getPaths, addExcludePaths, getExcludePaths, getFileExtension, setFileExtension will be deprecated and throw an exception when an iterator has been set.

In the end, this trait should perhaps be totally depreciated.

@greg0ire
Copy link
Member

greg0ire commented Aug 8, 2025

In the end, this trait should perhaps be totally depreciated.

In favor of a new trait? In any case, let us add the deprecations after the O[DR]M and bundles have been adapted to the new code please.

@rela589n
Copy link
Author

rela589n commented Aug 8, 2025

The constructor will accept string[]|Iterator.

That's an interesting statement. Using Iterator in favor of iterable has the benefit that we won't need to check the type of iterable manually (dir vs file). We can always know that the Iterator is the Iterator of file paths.

this trait should perhaps be totally depreciated.

Didn't get this either

@greg0ire
Copy link
Member

greg0ire commented Aug 8, 2025

The constructor will accept string[]|Iterator.

That's an interesting statement. Using Iterator in favor of iterable has the benefit that we won't need to check the type of iterable manually (dir vs file). We can always know that the Iterator is the Iterator of file paths.

Yeah but it will make the new way harder to use in trivial cases, which include tests.

this trait should perhaps be totally depreciated.

Didn't get this

I think @GromNaN means you could create a new trait instead of modifying the existing one.

@rela589n
Copy link
Author

rela589n commented Aug 8, 2025

Yeah but it will make the new way harder to use in trivial cases, which include tests.

It's as simple as creating a new object.

return new MyDriver(new ArrayIterator($filePaths));

rela589n added a commit to rela589n/doctrine-mongodb-odm that referenced this pull request Aug 8, 2025
In the scope of doctrine/persistence#429
(available from `doctrine/persistence` >= 4.1) there was added
`ColocatedMappingDriver::$sourceFilePathNames`, which allows
passing an iterable of file paths for the mapping driver to use.
This commit integrates those changes into `AttributeDriver`
(and `AnnotationDriver`).

Since `doctrine/orm` maintains the support
for `doctrine/persistence` of older versions, the `AttributeDriver`
ensures that `$sourceFilePathNames` is actually defined.
Tests use `InstalledVersions` to opt into new behaviour if
`doctrine/persistence` is at least version 4.1.

`AttributeDriver` now accepts a new boolean to opt into the new
behaviour. This flag is used to distinguish between an array of
directory paths (the existing way) and the iterable of file paths
(the new way). The old behaviour can be adapted into the new one by using `glob()` search on directory paths.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants