Skip to content

[Filesystem] Add a cross-platform readlink method #17498

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

Merged
merged 1 commit into from
Jul 30, 2016

Conversation

tgalopin
Copy link
Contributor

Q A
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets -
License MIT
Doc PR -

readlink() and realpath() have a completely different behavior under Windows and Unix:

  • realpath() resolves recursively the children links of a link until a final target is found on Unix and resolves only the next link on Windows ;
  • readlink() resolves recursively the children links of a link until a final target is found on Windows and resolves only the next link on Unix ;

I propose to solve this by implementing a helper method in the Filesystem component that would behave always the same way under all platforms.

*
* @throws IOException When the link does not exist or is not readable.
*/
public function readlink($path, $finalTarget = false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have defaulted to true here. Or even better, why not create 2 methods?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or just keep realpath, which is very useful. I'm not sure readlink is that useful anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you say realpath, what do you mean :) ? With final target to true ?

We are using both the "not final" and the "final" cases in Puli, I think two methods is a good idea indeed. How should we name them though? If we choose realpath/readlink, which convention do we choose? I would prefer the Unix one personnally (realpath = final, readlink = not final).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, the Linux convention seems the best one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did the change, but I think the name "realpath" is misleading. The PHP realpath do more things than resolving links (it also canonicalizes and normalizes the path: http://php.net/manual/fr/function.realpath.php). I'm not sure what happens in our case on Windows / Unix and if the behavior is really the same everywhere. Perhaps should we find a better name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of readLink for "not final" and resolveLink for "final"?

@tgalopin
Copy link
Contributor Author

Any opinion on the methods names here?

@tgalopin
Copy link
Contributor Author

I rebased this on master. What do you think if this feature?

@fabpot
Copy link
Member

fabpot commented Mar 4, 2016

I would use the method names from the Linux behavior.

}

if (!is_link($path)) {
return $path;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why shouldn't this method work on regular files/dirs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what you mean, but the support of non-links paths was something I found useful. This can be changed, I'm not sure what's the excepted behavior.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PHP's readlink() function will emit a warning if the given path is no link. I think we should throw an InvalidArgumentException then.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems appropriate indeed.

@tgalopin tgalopin force-pushed the readlink branch 3 times, most recently from 0b94a11 to f90548a Compare March 12, 2016 14:22
@tgalopin
Copy link
Contributor Author

I did the changes.

@xabbuh
Copy link
Member

xabbuh commented Mar 13, 2016

looks like you need to fix the tests

Status: Needs Work

@tgalopin
Copy link
Contributor Author

I fixed the tests

@tgalopin tgalopin force-pushed the readlink branch 3 times, most recently from af2ad79 to 583fdf1 Compare April 15, 2016 20:09
@tgalopin
Copy link
Contributor Author

I think this is ready, targeting 3.1. Any review?

throw new IOException(sprintf('The link %s does not exist and cannot be read.', $path));
}

if (!is_link($path)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this limitation ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what behavior we should have. The native behavior is to return false, but that seems inapropriate to me as we should return a string. What would you expect?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I understood, OK for exception here for me

@tgalopin
Copy link
Contributor Author

By canonicalized, do you mean absolute or also fully resolved link?

@nicolas-grekas
Copy link
Member

nicolas-grekas commented Apr 19, 2016

Both, ie what realpath does (on PHP/Linux)

@tgalopin tgalopin force-pushed the readlink branch 2 times, most recently from faa689c to 259b4dd Compare April 22, 2016 18:46
@fabpot
Copy link
Member

fabpot commented Jun 21, 2016

@tgalopin Any news on this PR? Is it worth finishing it?

@tgalopin
Copy link
Contributor Author

@fabpot I think it is, I'll work on it.

@tgalopin
Copy link
Contributor Author

tgalopin commented Jul 29, 2016

After some research, I propose the following behavior (same for every platform), following the way the readlink command works:

Signature: readlink($path, $canonicalize = false)

By default ($canonicalize = false) corresponding to command readlink <path>

  • if $path does not exist or is not a link, return null
  • if $path is link, return the value of the link (ie. the next direct target of the link, without consideration of existence)

With canonicalization ($canonicalize = true) corresponding to command readlink -f <path>

  • if $path does not exist, return null
  • if $path exists, return the absolute, fully resolved final target of the link (in the case of bar -> baz -> foo, readlink(bar) would return /absolute/path/to/foo)

Is everyone OK with this behavior? If it's ok for everyone, I'll start working on it.

@nicolas-grekas
Copy link
Member

nicolas-grekas commented Jul 29, 2016

Ok for me with this updated version (no exceptions but return null instead)

@tgalopin tgalopin force-pushed the readlink branch 6 times, most recently from 8efe4a5 to e79b1a5 Compare July 29, 2016 14:53
@tgalopin
Copy link
Contributor Author

I applied this behavior and tests are green, I think this is ready for review.

@tgalopin tgalopin force-pushed the readlink branch 2 times, most recently from fa3b7fd to 7ed04a4 Compare July 29, 2016 15:28
@nicolas-grekas
Copy link
Member

👍
Status: reviewed

@fabpot
Copy link
Member

fabpot commented Jul 30, 2016

Thank you @tgalopin.

@fabpot fabpot merged commit c36507e into symfony:master Jul 30, 2016
fabpot added a commit that referenced this pull request Jul 30, 2016
…lopin)

This PR was merged into the 3.2-dev branch.

Discussion
----------

[Filesystem] Add a cross-platform readlink method

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

`readlink()` and `realpath()` have a completely different behavior under Windows and Unix:

- `realpath()` resolves recursively the children links of a link until a final target is found on Unix and resolves only the next link on Windows ;
- `readlink()` resolves recursively the children links of a link until a final target is found on Windows and resolves only the next link on Unix ;

I propose to solve this by implementing a helper method in the Filesystem component that would behave always the same way under all platforms.

Commits
-------

c36507e [Filesystem] Add a cross-platform readlink/realpath methods for nested links
xabbuh added a commit to symfony/symfony-docs that referenced this pull request Sep 21, 2016
…(tgalopin)

This PR was merged into the master branch.

Discussion
----------

[Filesystem] Add documentation for the readlink method

Following the merge of symfony/symfony#17498, this PR introduces documentation for the new methd `readlink`.

Commits
-------

3572a3d [Filesystem] Add documentation for the readlink method
@fabpot fabpot mentioned this pull request Oct 27, 2016
@tgalopin tgalopin deleted the readlink branch November 8, 2019 10:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants